Selenium @FindBy vs driver.findElement()

pyrrhicVictory picture pyrrhicVictory · Aug 26, 2013 · Viewed 66k times · Source

Why should I use @FindBy vs driver.findElement()?

@FindBy forces me to move all my variables to a class level (when most of them only need to be at the method level). The only thing it seems to buy me is I can call PageFactory.initElements(), which handles lazy initialization for me.

What am I missing?

Answer

acdcjunior picture acdcjunior · Aug 26, 2013

Roughly speaking, @FindBy is just an alternate way of finding elements (the "usual way" being driver.findElement() as you said).

The big advantage of that annotation is not itself, though. It is better used to support the PageObject pattern.

In a few words, the PageObject pattern tells you to create a class for each page of the system you are trying to use/test.

So, instead of having (the usual driver.findElement() code):

public class TestClass {
    public void testSearch() {
        WebDriver driver = new HtmlUnitDriver();
        driver.get("http://www.google.com/");
        Element searchBox = driver.findElement(By.name("q"));
        searchBox.sendKeys("stringToSearch");
        searchBox.submit();
        // some assertions here
    }
} 

You'd define a class for the page (with the @FindBy annotation for the elements used):

public class GooglePage {
    @FindBy(how = How.NAME, using = "q")
    private WebElement searchBox;
    public void searchFor(String text) {
        searchBox.sendKeys(text);
        searchBox.submit();
    }
}

And use it like:

public class TestClass {
    public void testSearch() {
        WebDriver driver = new HtmlUnitDriver();
        driver.get("http://www.google.com/");
        GooglePage page = PageFactory.initElements(driver, GooglePage.class);
        page.searchFor("stringToSearch");
        // some assertions here
    }
} 

Now, I know this may seem verbose at first, but just give it a moment and consider having several tests cases for that page. What if the name of the searchBox changes? (From the name "q" to an id, say query?)

In what code there would be more changes to make it work again? The one with or the one without the Page Objects (and @FindBy)? If a page changes its structure a lot, in what code the maintenance would be easier?

There are some other advantages, such as additional annotations like:

@FindBy(name = "q")
@CacheLookup
private WebElement searchBox;

That @CacheLookup makes the lookup for the element happen just once. After that, it will be cached in the variable and accessible much faster.

Hope this helps. For more details, make sure to check PageFactory and the PageObject pattern.