Access the By class created using @FindBy annotation in Selenium

Recently I needed to increase the information the NoSuchElementException contained when using Selenium’s @FindBy annotation. During this I stumbled across a way to get access to the By class created from the @FindBy.

For example say we have the following PageObject


public class testPageObject {
   @FindBy(css = "h1[name='test']")
   WebElement testElement1;

   @FindBy(xpath = "//*[class='test']")
   WebElement testElement2;

   @FindBy(id = "test")
   WebElement testElement3;


   public testPageObject() {
       //Cache the WebElements using a 5 second wait
       PageFactory.initElements(new AjaxElementLocatorFactory(driver, 5), this);
   }

   public void printBy() {
        By testElement1 = getBy("testElement1");
        By testElement2 = getBy("testElement2");
        By testElement3 = getBy("testElement3");

        System.out.println(testElement1);
        System.out.println(testElement2);
        System.out.println(testElement3);
   }

   private By getBy(String fieldName) {
       try {
            return new Annotations(this.getClass().getDeclaredField(fieldName)).buildBy();
       } catch (NoSuchFieldException e) { return null; }
   }
}

The solution is to use Selenium’s built in Annotations class which takes a field and call buildBy() to return the By class for that field.

The output of the printBy() function:

By.selector: h1[name=’test’]
By.xpath: //*[class=’test’]
By.id: test

7 responses to “Access the By class created using @FindBy annotation in Selenium

  1. Hey, falkenfighter! Thanks, very useful. Is it possible to change xpath value in @FindBy? Say, I have the following annotation:

    @FindBy(xpath=”//td[contains(text(),’$SystemName’)]”)
    private WebElement RequiredSystemName;

    In runtime, I want to replace $SystemName with the real value, say, “Firestone”. Is it doable? I mean, with your code in the article, I can get By.xpath: //td[contains(text(),’$SystemName’)]. But can I change xpath’value? Thanks.

  2. It looks like I found the solution. I don’t change xpath value in @FindBy (still not sure if it is possible), but create the new one:
    private WebElement prepareWebElementWithDynamicXpath (String xpathValue, String substitutionValue ) {

    return driver.findElement(By.xpath(xpathValue.replace(“xxxxx”, substitutionValue)));
    }

    • If you use the @Findby annotation Java requires all variables to be constant. So trying to place a dynamic variable into the annotation will not build. What I usually end up doing is target a higher ‘static’ element within the DOM and find that in the @Findby. Once you have the PageObject initialized with the static element find the dynamic element within the function. It’s not the best solution but provides a workaround.


      // Find the static parent of the dynamic element we want to work with
      @Findby(css = ".static-element")
      WebElement myStaticElement;

      // Once we have the static parent, find the dynamic element.
      public void clickDynamicElement() {
      String dynamicClass = ".dynamic-element";
      myStaticElement.findElement(By.css(dynamicClass)).click();
      }

      • Falkenfighter, thanks! So no silver bullet. Do I understand right that for any dynamic element you have to actually take care of two elements, both parent and child? Since I am a kind of newbie, may I ask why it is better than creating WebElement from scratch based on the value of a dynamic variable (you know it at runtime)? Thanks, Racoon.

      • The only reason I would create the parent element is that when the PageObject loads I’m guaranteed the initial elements are in the DOM. Sort of a fail fast design. But I’ve seen PageObjects built many ways and creating the WebElement from scratch is just as viable.

Leave a comment