Selenium @FindBy vs driver.findElement()
Asked Answered
D

7

39

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?

Danube answered 26/8, 2013 at 3:15 Comment(0)
M
68

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.

Most answered 26/8, 2013 at 3:52 Comment(4)
The PageObject pattern seems very helpful! But, can't I use that pattern just as well with .findElement?Danube
Yes, you definitely could.Just notice you'd have to call .findElement() and initialize the elements (into the classes' fields) yourself before using them. That's just what the @FindBy and .initElements() do to you (and other minor stuff, as pointed in the answer). Calling it yourself would work, it'd just be more verbose and have more points of maintenance.(Also, if you use other frameworks built on top of selenium, like Arquillian Drone/Graphene you wouldn't even have to call .initElements(), but that's another story.)Most
I feel like this PageObject pattern doesn't map well to single page apps. You perform some action, and you're on the same page and some of it is different. Should that be a new PageObject, or the same one? Things get kind of messy.Zhukov
@DanielKaplan I don't fee like there are too many special things to SPAs to render the PO useless. Should that be a new PageObject, or the same one? -> I know it is not the answer you expected but: it depends... In the end, if it looks simpler to create a PO for each "state" of the SPA, then do. If it is working to cram everything into a single one, then OK as well.Most
S
4

In simple words, both @FindBy and driver.findElement() are different approach to locate element/s through different Locator Strategies.

When using PageFactory we can use the Annotation Type FindBy. The FindBy annotations helps us to remove the boiler-plate code which we generally use in the form of findElement() and findElements() when looking for elements.

As an example:

WebElement element = driver.findElement(By.name("q"));
element.click();

becomes:

element.click();

You can find @Simon Stewart's comments on the same topic within the discussion How to use explicit waits with PageFactory fields and the PageObject pattern


References

You can find a couple of relevant detailed discussion in:

Synclastic answered 18/1, 2019 at 10:10 Comment(0)
S
2

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.

Sturges answered 22/2, 2018 at 16:8 Comment(2)
I agree, I did some effort on this, see my question: intellij-support.jetbrains.com/hc/en-us/community/posts/… and links there and youtrack.jetbrains.com/issue/IDEA-183120Premundane
update: related issue youtrack.jetbrains.com/issue/IDEA-195166 is now resolved so it will be possible to create structural search pattern to find elements with duplicated selectros. Ah sorry, I just realised that it's not so relevant to your post but it's good improvement.Premundane
C
2

One of the advantages of using Page Factory is, it can avoid StaleElementException. Please see the below link:

How Page Object Model solves StaleElementReferenceException?

Excerpts from the above link:

You will see programs run fine when we use page object design pattern. You will never get Stale Element Reference Exception. When we use FindBy annotation in POM, webdriver locates element and update reference every time before performing any action on that. You can use web element without relocating any where. This is major advantage of using Page Object Model.


How to fight and avoid Stale Elements.

Excerpts from the above link:

How to fight and avoid Stale Elements

There are many ways of dealing with Stale Element Exception on the web. Here I gathered the ones that I personally find most useful.

A good practice is to use @FindBy annotation because of Lazy Initialization This way elements will be initialized right before actual usage. Example: @FindBy(xpath=”someXpath” ) public WebElement someElement;

Use wait methods for JavaScript, Ajax, Jquery etc. This will solve the “Race condition” that causes this exception to occur.

Companionate answered 18/12, 2018 at 23:56 Comment(0)
S
0

I see one more advantage to use @FindBy. When you have two methods in the same class which have the same name, only return different page objects and passed parameter: driver. Then you can use in PageObject:

public <T extends TestingEnvironment > T clickAction(Class<T> expectedPage){
            element.click();
            return PageFactory.initElements(driver, expectedPage);
        }

Test method:

public void Test() {
        TestingEnvironment testingEnvironment = PageFactory.initElements(driver, TestingEnvironment.class);

        testingEnvironment.openPage();

        Patient patient = testingEnvironment.logIn();

        Reason reason = patient.clickAction(Reason.class);
        //You also can assing:
        NewReason newReason =  patient.clickAction(NewReason.class);

    }
Scenery answered 23/11, 2019 at 15:38 Comment(0)
C
0

It is important that you initialize your WebElement with the PageFactory from Selenium.

i.e

...
@FindBy(id = "__foo_id")
WebElement login;
...
    
System.setProperty("webdriver.chrome.driver","./drivers/chromedriver.exe");
webDriver = new ChromeDriver();
webDriver.get("https://stackoverflow.com");
PageFactory.initElements(webDriver,this);
Catarina answered 11/10, 2022 at 20:37 Comment(0)
K
-2
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
public class CommonPageForStudent {

    @FindBy(name="usname")
    private WebElement Studentusername;
    @FindBy(name="pass")
    private WebElement Studentpassword;
    @FindBy(xpath="//button[@type='submit']")
    private WebElement StudentLetmein;
    @FindBy(id="logoutLink")
    private WebElement StudentlogoutLnk;
    public  void loginToStudent(String username , String password ){
        Studentusername.sendKeys(username);
        Studentpassword.sendKeys(password);
        StudentLetmein.click();

       }

    //when you call this methods from another class write this 2 line code that class
    //CommonPageForStudent page = PageFactory.initElements(driver,       CommonPageForStudent.class);
    // page.loginToStudent("","");

    public void logOut(){
        StudentlogoutLnk.click();
    }
    //page.logOut(); method call

}`
Kettledrummer answered 13/10, 2016 at 10:52 Comment(1)
Could you add some explanation to this code please?Correna

© 2022 - 2024 — McMap. All rights reserved.