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?
Selenium WebDriver defines two methods for identifying the elements, they are findElement and findElements . findElement: This command is used to uniquely identify a web element within the web page. findElements: This command is used to uniquely identify the list of web elements within the web page.
There are differences between findElement and findElements in the Selenium webdriver. Both of them can be used to locate elements on a webpage. The findElement points to a single element, while the findElements method returns a list of matching elements.
driver. findElement() returns first matching element with specified locator and driver. findElements() returns all(array of) matching elements with specified locator.
Annotation Type FindBy Used to mark a field on a Page Object to indicate an alternative mechanism for locating the element or a list of elements. Used in conjunction with PageFactory this allows users to quickly and easily create PageObjects. It can be used on a types as well, but will not be processed by default.
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.
I don't like @FindBy annotations because then IntelliJ no longer detects if that variable is being used, makes it a pain to clean up.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With