How to Use @FindBy Annotation in Selenium Java
Tags: javascript, selenium, findbyannotation
Published: 2023-11-16 06:36:38

Code maintenance and duplication are two common challenges that software teams often face, resulting in reduced performance and reliability. It also makes the code more complex, tightly coupled, and less flexible for refactoring. With all these problems, adding new lines of code to the software becomes quite tricky as we need to think of a logic to fit those lines within the code.

In automation [test suites](https://www.lambdatest.com/learning-hub/test-suite?utm_source=devto&utm_medium=organic&utm_campaign=nov_16&utm_term=ap&utm_content=learning_hub) as well, we all face the same problem. The code becomes too complex and bulky. Even for adding a simple test for new functionality, we need to think twice and find a solution for checking which part of the code needs to be updated so it doesn’t hamper the existing working of the test suite. Sometimes, we have to repeat the same line of code multiple times because it is how the automation framework was designed to work.

Today, there are multiple [automation test frameworks](https://www.lambdatest.com/blog/automation-testing-frameworks/?utm_source=devto&utm_medium=organic&utm_campaign=nov_16&utm_term=ap&utm_content=blog) that help teams to automate tests and help in delivering quality software. [Selenium WebDriver](https://www.lambdatest.com/learning-hub/webdriver?utm_source=devto&utm_medium=organic&utm_campaign=nov_16&utm_term=ap&utm_content=learning_hub) is a popular web automation framework that helps automate the tests for popular modern browsers like Google Chrome, Mozilla Firefox, Microsoft Edge, Safari, etc. To learn more about Selenium WebDriver, check out this guide on [What is Selenium?](https://www.lambdatest.com/selenium?utm_source=devto&utm_medium=organic&utm_campaign=nov_16&utm_term=ap&utm_content=webpage)

Writing automated tests using Selenium WebDriver is easy and simple. However, software testing teams face a bigger challenge: keeping the test implementation separate from the web page elements used in the tests.

[Page Object Model](https://www.lambdatest.com/blog/selenium-java-testing-page-object-model/?utm_source=devto&utm_medium=organic&utm_campaign=nov_16&utm_term=ap&utm_content=blog), also called POM, is a popular design pattern in Selenium WebDriver that creates a page object repository to store WebElements separately in different classes to keep the tests clean and more readable.

***Get your CSS validated by our*** [***CSS Validator***](https://www.lambdatest.com/free-online-tools/css-validator?utm_source=devto&utm_medium=organic&utm_campaign=nov_16&utm_term=ap&utm_content=free_online_tools) ***and eliminate syntax errors and other issues that could affect your website’s performance. Ensure your CSS code is error-free today.***

Design patterns help us overcome industry-standard approaches to solving a recurring problem and help us save time once we sensibly use the design pattern. Design patterns speed up development and are known to fix the problems before they are known in the implementation process. It also facilitates code maintenance, helps us avoid code duplication, and facilitates code readability.

[Page Factory Model](https://www.lambdatest.com/blog/page-factory-in-selenium/?utm_source=devto&utm_medium=organic&utm_campaign=nov_16&utm_term=ap&utm_content=blog) is another popular design pattern in Selenium. It is considered an Optimized Page Object Model that uses the *PageFactory* class in Selenium.

In this blog on using *@FindBy* annotation in Selenium, which is part of the Page Factory Model in Selenium WebDriver, we will also learn how to use *@FindBys, @FindAll,* and *@CacheLookup* annotations in [Selenium Java](https://www.lambdatest.com/learning-hub/selenium-java?utm_source=devto&utm_medium=organic&utm_campaign=nov_16&utm_term=ap&utm_content=learning_hub).

### What is Page Factory in Selenium WebDriver?

Page Factory is a way of implementing the Page Object Model that uses the *PageFactory* class. By using the *PageFactory* class in Selenium, testers can interact with web elements, making it robust with enhanced efficiency and simplicity in using Page Objects.

The following image shows the difference between the Page Object and Page Factory Model:

![image](https://cdn-images-1.medium.com/max/800/0*8eCrNSmTPRsumoM8.jpg)

To use the Page Factory Model, we need annotations like *@FindBy, @FindBys,* and *@FindAll* for locating WebElement in the Page Object class. Next, we need to initialize the Page Object class using the *initElements()* method from the *PageFactory* class.

{% youtube zI7Xbaj4RIs %}

***Protect your XML data with our online free*** [***XML Escape***](https://www.lambdatest.com/free-online-tools/xml-escape?utm_source=devto&utm_medium=organic&utm_campaign=nov_16&utm_term=ap&utm_content=free_online_tools) ***tool. Quickly escape your code and be assured from vulnerabilities with just a few clicks.***

Let’s learn how to use the *@FindBy, @FindBys,* and *@FindAll* annotations to use the Page Factory Model efficiently.

![image](https://cdn-images-1.medium.com/max/800/0*-yd1r7G7x9L5vkJ0.png)

### Using @FindBy annotation in Selenium WebDriver

*@FindBy* annotation marks a field on the Page Object for locating the *WebElement* or a list of *WebElements*. It is used in coordination with the *PageFactory* class that allows users to easily and quickly create Page Objects.

There are two different implementation ways for using *@FindBy* annotation.

* Using “How” and “Using” Strings
    
* Passing Locator Strategy
    

### Using “How” and “Using” Strings

The first way to implement *@FindBy* annotation is by using the “how” and “using” strings as parameters to the annotation by declaring a WebElement variable. The values in the parameter can be supplied as follows –

* “how” — With the “how” enum in this parameter, you have the flexibility to utilize any of the following [locator strategies](https://www.lambdatest.com/learning-hub/selenium-locators?utm_source=devto&utm_medium=organic&utm_campaign=nov_16&utm_term=ap&utm_content=learning_hub):
    
* ID
    
* Name
    
* ClassName
    
* LinkText
    
* PartialLinkTex
    
* TagName
    
* CSS
    
* XPath
    
* ID or Name
    
* “using” — In this parameter, the actual selector needs to be supplied as per the locator strategy selected in the “how” parameter.
    

**The syntax for using *@FindBy* annotation \[Passing How enum\]:**

![image](https://cdn-images-1.medium.com/max/800/0*9ubk_72pwEi3gNko.png )

#### Demo: @FindBy using How enum

Let’s take an example of the [Simple Form Demo screen](https://www.lambdatest.com/selenium-playground/simple-form-demo?utm_source=devto&utm_medium=organic&utm_campaign=nov_16&utm_term=ap&utm_content=webpage) on LambdaTest’s [Selenium Playground](https://www.lambdatest.com/selenium-playground/?utm_source=devto&utm_medium=organic&utm_campaign=nov_16&utm_term=ap&utm_content=webpage) website and locate the “*Enter Message*” field.

![image](https://cdn-images-1.medium.com/max/800/0*4swMLsIYhA80DDsf.png )

In this screen for the *Enter Message* field, we can use the ID locator — “*user-message*” as a selector to locate the field. So, to use it in the code with *@FindBy* annotation, we can write it as follows:

```plaintext
@FindBy(how = How.ID, using="user-message")
private WebElement enterMessageField;
```

***Catch errors before they impact your*** [***JavaScript validator***](https://www.lambdatest.com/free-online-tools/js-validator?utm_source=devto&utm_medium=organic&utm_campaign=nov_16&utm_term=ap&utm_content=free_online_tools) ***code. Our validator and linter enforces coding standards and helps you catch errors. Validate your code today.***

Once we have defined the WebElement, we can directly use it in the methods to act on the element. The following lines of code show how to interact with the WebElement defined using *@FindBy* annotation:

```plaintext
public void enterMessage() {
this.enterMessageField.sendKeys("This is a test message");
}
```

However, to make this work, we need to initialize the *initElements()* method of the PageFactory class. *intiElements()* is a static method in the Page Factory class of Selenium that is used to initialize all web elements located by *@FindBy* annotation. It can be done in every Page Object class where we want to use the *@FindBy* annotation.

```plaintext
public SimpleFormPage(final WebDriver driver) {
this.driver = driver;
PageFactory.initElements(driver, this);
}
```

We will be initializing the *initElements()* method in the constructor of the *SimpleFormPage* class that holds all the page objects for the Simple Form page. Similarly, the *initElements()* method will have to be initialized in other Page Object classes as well.

Let’s now learn the other way of implementing *@FindBy* annotation.

### Passing Locator Strategy

The second way to implement *@FindBy* annotation is to directly pass on the locator strategy and its respective selector as a parameter to the annotation by declaring a WebElement variable.

**The syntax for using *@FindBy* annotation \[passing locator strategy\]:**

![image](https://cdn-images-1.medium.com/max/800/0*BuO65lAjVFK9YfKU.png)

Any locator strategy values can be passed in the parameter:

* ID
    
* Name
    
* ClassName
    
* LinkText
    
* PartialLinkText
    
* TagName
    
* CSS
    
* XPath
    

The above-mentioned locator strategy values are case-sensitive and must be used in small letters only.

#### Demo: @FindBy by directly passing the locator strategy

Let’s take an example of the [Simple Form Demo screen](https://www.lambdatest.com/selenium-playground/simple-form-demo?utm_source=devto&utm_medium=organic&utm_campaign=nov_16&utm_term=ap&utm_content=webpage) on LambdaTest’s [Selenium Playground](https://www.lambdatest.com/selenium-playground/?utm_source=devto&utm_medium=organic&utm_campaign=nov_16&utm_term=ap&utm_content=webpage) website and locate the “*Get Checked Value*” button.

![image](https://cdn-images-1.medium.com/max/800/0*vVRZJyRvYLdBTY1r.png )

As per the DOM of the page, we can use the *id* — “*showInput*” as a selector for locating the Get *Checked Value* button. Let’s do this by using the *@FindBy* annotation and writing the following code:

```plaintext
@FindBy(id = "showInput")
private WebElement getCheckedValueBtn;
```

Similarly, we can use other locator strategies like name, CSS, XPath, etc.

***Keep your JavaScript code safe from syntax errors with our free online*** [***JavaScript Escape***](https://www.lambdatest.com/free-online-tools/js-escape?utm_source=devto&utm_medium=organic&utm_campaign=nov_16&utm_term=ap&utm_content=free_online_tools) ***tool by quickly and easily converting special characters in your JavaScript.***

Once we have defined the WebElement variable for locating the WebElement using *@FindBy*, we can directly use it to perform interaction with the button.

```plaintext
public void clickButton() {
this.getCheckedValueBtn.click();
}
```

Don’t forget to initialize the *intiElements()* method from the *PageFactory* class to make the annotation work.

**Using *@FindBy* annotation for finding multiple WebElements:**

*@FindBy* annotation can also be used to return a list of WebElements if we want a list of elements to be returned using a single selector. We must define the variable that returns List&lt;*WebElement*\&gt; using *@FindBy* annotation.

We can use either of the implementations as we discussed above for returning a list of WebElements using *@FindBy*.

**The syntax for returning a list of WebElements using Implementation 1:**

![image](https://cdn-images-1.medium.com/max/800/0*ulFu-vbbOK5HmRcy.png )

**The syntax for returning a list of WebElements using locator strategy in *@FindBy* annotation:**

![image](https://cdn-images-1.medium.com/max/800/0*8DDF_wdzauwjSbxj.png )

For better understanding, let’s check out a practical implementation example for returning a list of WebElements.

#### Demo: @FindBy by directly passing locator strategy for finding multiple WebElements

Let’s take an example of the LambdaTest Selenium Playground website and check the total number of menu options on the main page.

![image](https://cdn-images-1.medium.com/max/800/0*TryQ6iBapzLpHQp3.png )

There are a total of 37 menus displayed on this page. Let’s locate all these menus and return their list using a *@FindBy* annotation with the *CSS* [*Selector* locator strategy](https://www.lambdatest.com/learning-hub/css-selectors?utm_source=devto&utm_medium=organic&utm_campaign=nov_16&utm_term=ap&utm_content=learning_hub) in Selenium.

![image](https://cdn-images-1.medium.com/max/800/0*97DzwRwJjgSueqcJ.png)

The CSS Selector “*.container\_\_selenium ul li a*” can be used to return a list of WebElements for the menus. Let’s use this selector with *@FindBy* annotation.

```plaintext
@FindBy(css = ".container__selenium ul li a")
private List<WebElement> menus;
```

List of menus can also be returned using the How enum in *@FindBy* annotation as follows:

```plaintext
@FindBy(how = How.CSS, using =".container__selenium ul li a")
private List<WebElement> menus;
```

The following method will return the size of the list that can be used in the test to verify that the list has 37 items in it.

```plaintext
public int menuListSize () {
return this.menus.size();
}
```

Following is the Test to check the total number of menus in the *menus* list.

```plaintext
public class SeleniumPlaygroundTests extends BaseTest {

@Test
public void testMenus() {
this.driverManager.getDriver().get("https://www.lambdatest.com/selenium-playground/");
final var mainPage = new MainPage(this.driverManager.getDriver());
assertEquals(mainPage.menuListSize(), 37);
}
```

Following screenshot of the [test execution](https://www.lambdatest.com/learning-hub/test-execution?utm_source=devto&utm_medium=organic&utm_campaign=nov_16&utm_term=ap&utm_content=learning_hub) from IntelliJ shows that the tests executed successfully.

***Convert your JSON files to CSV format in seconds with our easy-to-use*** [***JSON to CSV***](https://www.lambdatest.com/free-online-tools/json-to-csv?utm_source=devto&utm_medium=organic&utm_campaign=nov_16&utm_term=ap&utm_content=free_online_tools) ***Converter. It’s fast, reliable, and user-friendly, making data conversion simple.***

![image](https://cdn-images-1.medium.com/max/800/0*07S9zKWnUzS7fRSB.png)

This confirms that the list of menus has 37 items in it, and the *@FindBy* annotation we used to return a list of *menu* WebElements works perfectly fine.

This confirms that the list of menus has 37 items in it, and the *@FindBy* annotation we used to return a list of menu WebElements works perfectly fine.

### Using @CacheLookup annotation in Selenium WebDriver

*@CacheLookup* annotation helps to cache the WebElement. When this annotation is applied over a WebElement, Selenium WebDriver keeps the cache of the WebElement instead of searching for it every time from the WebPage when the test is run. This helps in saving [test execution time](https://www.lambdatest.com/blog/test-execution-time/?utm_source=devto&utm_medium=organic&utm_campaign=nov_16&utm_term=ap&utm_content=blog).

**When to use @CacheLookup?**

It is beneficial to use *@CacheLookup* annotation on the WebElements, which does not change when on the web page once it is loaded. The test speed and performance can be improved by using this annotation on the static elements on the web page.

**When not to use @CacheLookup?**

It is not recommended to use the *@CacheLookup* annotation for the WebElements that are dynamic and keep changing every time the page loads. Dynamic elements refer to the WebElement like Timer text that keeps changing every second. Another example would be a capital market website that shows the price of the stocks that keeps on changing every second or minute as per the stock trading. For such WebElements, if *@CacheLookup* annotation is used, we may get a [*Stale Element Exception*](https://www.lambdatest.com/blog/handling-stale-element-exceptions-in-selenium-java/?utm_source=devto&utm_medium=organic&utm_campaign=nov_16&utm_term=ap&utm_content=blog).

**The syntax for using @CacheLookup annotation:**

![image](https://cdn-images-1.medium.com/max/800/0*fdgniFjqQuCKg8Tp.png)

***From*** [***SQL to JSON***](https://www.lambdatest.com/free-online-tools/sql-to-json?utm_source=devto&utm_medium=organic&utm_campaign=nov_16&utm_term=ap&utm_content=free_online_tools) ***in a snap! Convert SQL queries to JSON effortlessly with our SQL to JSON converter, enhancing compatibility and data interchange. Try it now.***

### Demo: @CacheLookup annotation

The following test scenario will be used to check out the working of the *@CacheLookup* annotation. We will run the test using the *@CacheLookup* annotation and run the same test by removing the *@CacheLookup* annotation. Next, we will check the performance by comparing the test execution time.

The following test scenario will be used to demo the working of *@CacheLookup* annotation, where the performance will be checked by running the tests after using *@CacheLookup* annotation over WebElements and removing them.

**Test Scenario**

1. Navigate to the Table Search Filter window on the LambdaTest’s Selenium Playground website.
    
2. Get the page header text and cache it using *@CacheLookup* annotation.
    
3. Perform assertion on the page’s header by getting its text.
    
4. Click on the Filter button and enter filter values for first name, username, and last name fields.
    
5. On retrieving the records, print the values of the first record on the console.
    
6. Perform the Test Execution and record the time after using *@CacheLookup*.
    
7. Remove the *@CacheLookup* annotation, rerun the tests, and check the time for test execution.
    

![image](https://cdn-images-1.medium.com/max/800/0*Rg8Q30ee0mG-ob50.png )

We will be using the *@FindBy* annotation, as we discussed in the earlier section, to locate the button and the fields.

The following class is created for housing all the WebElements on the Table Search page.

Filename: [TableSearchPage](https://github.com/mfaisalkhatri/selenium-pagefactory-demo/blob/main/src/test/java/io/github/mfaisalkhatri/pages/seleniumplayground/TableSearchPage.java)

![image](https://cdn-images-1.medium.com/max/800/0*Dk50OTIvc5euE-oL.png )

We will be caching every WebElement that we need to interact on this page. The following table shows the variable names used for the respective fields using the *@FindBy* annotation and *@CacheLookup* annotation to cache the WebElement.

<table>
    <tr>
        <td>FIELD NAME ON THE WEB PAGE</td>
        <td>VARIABLE NAME IN THE TABLESEARCH CLASS FOR THE WEBELEMENT </td>
    </tr>
    <tr>
        <td>Page Header</td>
        <td>pageHeader </td>
    </tr>
    <tr>
        <td>First Name field</td>
        <td>firstNameField </td>
    </tr>
    <tr>
        <td>Last Name field</td>
        <td>lastNameField </td>
    </tr>
    <tr>
        <td>User Name field</td>
        <td>userNameField </td>
    </tr>
    <tr>
        <td>Table Row retrieved after applying filter</td>
        <td>tableRecord</td>
    </tr>
</table>

***Don’t let encoding IDNs slow you down. Save time and hassle with our*** [***IDN Encode***](https://www.lambdatest.com/free-online-tools/idn-encode?utm_source=devto&utm_medium=organic&utm_campaign=nov_16&utm_term=ap&utm_content=free_online_tools) ***free online tool to quickly and easily converts internationalized domain names. Try it now.***

![image](https://cdn-images-1.medium.com/max/800/0*hGSO7uO-g2OVedAo.png )

The *pageHeaderText()* method will return the text value of the page header that will be used in the test for assertion.

The *searchForRecord()* method will click on the *Filter* button and allow entering values in the *Username, First Name,* and *Last Name* fields.

![image](https://cdn-images-1.medium.com/max/800/0*qRx54KfoLfen8BeB.png )

Next, we need to get the text of the record that is fetched after applying the filter. The *tableRecordValues()* method will fetch the text of the record that will be retrieved after applying the filter values.

![image](https://cdn-images-1.medium.com/max/800/0*aSGKVXOoAkhd-jpo.png )

Let’s now move ahead and write the test where we will navigate to the *Table Search* page, perform the assertion for the page header text, retrieve the table record after applying a filter, and print the record fetched.

The *testTableSearch()* method is created for verifying the test scenario.

Filename: [SeleniumPlaygroundTests](https://github.com/mfaisalkhatri/selenium-pagefactory-demo/blob/main/src/test/java/io/github/mfaisalkhatri/tests/SeleniumPlaygroundTests.java)

![image](https://cdn-images-1.medium.com/max/800/0*-GZbIgrhMoalwpAY.png )

***Note:*** *I have declared two variables, startTime and endTime, to demo the test execution time in milliseconds.*

***Need to make your JSON code more readable? Our online*** [***JSON Prettifier***](https://www.lambdatest.com/free-online-tools/json-prettify?utm_source=devto&utm_medium=organic&utm_campaign=nov_16&utm_term=ap&utm_content=free_online_tools) ***tool makes your JSON data more human-readable. Copy, paste, and prettify in seconds.***

**Test Execution**

We will be running the test twice and record the time taken to run the test. The first run will be using the *@CacheLookup* annotation, and the second will be without using the *@CacheLookup* annotation. This will help us compare the test execution time with and without cache.

**Test Execution with @CacheLookup annotation**

Here is the screenshot of the test execution using the testng.xml file with *@CacheLookup* annotation.

![image](https://cdn-images-1.medium.com/max/800/0*f1tWJ8WfcSEWrl5x.png )

We can see that it took 481 milliseconds to run the tests.

**Test Execution without @CacheLookup annotation**

Before we execute the tests, let’s comment out all the *@CacheLookup* annotations in the *TableSearchPage* class.

![image](https://cdn-images-1.medium.com/max/800/0*gD_b-9Q_ls9wYLWo.png)

Let’s execute the test again and check the time taken to execute the test.

![image](https://cdn-images-1.medium.com/max/800/0*nJFML13Rjn2DjgMI.png )

It can be seen that it took 534 milliseconds to run the test. So, if we compare the time taken to run the test with and without using *@CacheLookup* annotation, it appears to be a difference of 53 milliseconds. As the test scenario was pretty straightforward, hence it is a steep difference here. However, if we take a complex scenario, it can be a major difference in seconds or minutes as well.

From this, we can conclude that using *@CacheLookup* annotation can save the test execution time, eventually helping us in quicker builds.

***Minify your JS code without changing its functionality with our easy-to-use*** [***JavaScript Minifier***](https://www.lambdatest.com/free-online-tools/js-minify?utm_source=devto&utm_medium=organic&utm_campaign=nov_16&utm_term=ap&utm_content=free_online_tools) ***that reduces the size of your scripts and improve website speed.***

### Using @FindBys annotation in Selenium WebDriver

*@FindBys* annotation is used to locate a WebElement or list of WebElements with more than one selector. The condition in using *@FindBys* is that all of the selectors given in the *@FindBys* should match the respective WebElement(s) criteria.

It is an “AND” condition between all the selectors given in the annotation. This is helpful in cases where we don’t have a unique locator for the WebElement that we need to interact with on the web page.

**The syntax for using *@FindBys* annotation:**

![image](https://cdn-images-1.medium.com/max/800/0*o8_eM-Hy9qu7XCBU.png )

To implement *@FindBys*, we must pass on the multiple *@FindBy* statements as parameters and declare the WebElement or List&lt; WebElement &gt; variable.

It should be noted that *@FindBy* annotation can be used either by providing the How enum or by supplying the locator strategy directly. In the screenshot above, both ways have been shown to use the *@FindBy* while using the *@FindBys* annotation.

However, one more thing to note while using *@FindBys* annotations is that we need to provide the locators considering the parent-child relationship, using the attributes within the HTML tag won’t work with *@FindBys* annotation. The following example implementation will help in understanding in detail.

**Implementation Example**

Let’s take an example of the [Radio button demo](https://www.lambdatest.com/selenium-playground/radiobutton-demo?utm_source=devto&utm_medium=organic&utm_campaign=nov_16&utm_term=ap&utm_content=webpage) page on LambdaTest’s Selenium Playground website.

![image](https://cdn-images-1.medium.com/max/800/0*Vg_WdVPmq015ovrQ.png )

On this page, we will be locating the “*Male*” radio button by using the *@FindBys* annotation.

As we can see in the DOM, the class name “*container*” is the parent of the input tag with *type, value*, and *name* attributes for the *Male* radio button field. Similarly, if we check out the “*Female*” radio button, it also has the same hierarchy.

![image](https://cdn-images-1.medium.com/max/800/0*dyip6X1jcw77JsSk.png)

Even the *name* attribute is the same for both the radio buttons. The only difference in selector between *Male* and *Female* radio buttons is the value attribute, i.e., *value=”Male”* for Male radio button and *value = “Female”* for *Female* radio button.

As the locator is not unique here, we can follow the parent-child relation approach to locate the element by locating the parent first and then approaching to find the child element using the respective locators.

***Transform your messy CSS code into beautiful and organized code with our user-friendly and efficient online free*** [***CSS Prettifier***](https://www.lambdatest.com/free-online-tools/css-prettify?utm_source=devto&utm_medium=organic&utm_campaign=nov_16&utm_term=ap&utm_content=free_online_tools) ***tool with just a few clicks.***

![image](https://cdn-images-1.medium.com/max/800/0*tv_0mBSIkW1A4BrB.png)

The following lines of code will help us locate the *Male* radio button using the *@FindBys* annotation in Selenium:

```plaintext
@FindBys({
@FindBy(how = How.CLASS_NAME, using = "container"),
@FindBy(css = "input[value=\"Male\"]")
})
private WebElement maleRadioBtn;
```

Using the *@FindBys* annotation, first the parent WebElement with class name i.e. “*container*” is located and then the child element using the input tag with *value* attribute “*Male*” is used to finally locate the WebElement for *Male* radio button.

Similarly, *@FindBys* can locate multiple WebElements on the web page. The only change required is instead of WebElement we need to pass List&lt;*WebElement*\&gt; as show in the image below:

![image](https://cdn-images-1.medium.com/max/800/0*nX46fUhcddlKewva.png)

### Demo: @FindBys annotation

Let’s take an example of the [Table Pagination demo](https://www.lambdatest.com/selenium-playground/table-pagination-demo?utm_source=devto&utm_medium=organic&utm_campaign=nov_16&utm_term=ap&utm_content=webpage) page on LambdaTest’s Selenium Playground website. On this page, let’s locate the total number of columns in the table using the *@FindBys* annotation.

![image](https://cdn-images-1.medium.com/max/800/0*SkfPnHd7FOp4Q3Ku.png )

As we can see in the DOM, the parent WebElement for the table is its id — “*table-id*”. We can use it for locating the complete table. Now, to find the number of columns, it can be easily done by finding the column headers and returning their size.

The column headers of the table can be located using the CSS Selector — “*thead &gt; tr &gt; th*” Using *@FindBys* annotation, we can return list of WebElements for the column headers as follows:

```plaintext
@FindBy(how = How.ID, using = "table-id"),
@FindBy(css = "thead > tr > th")
})
private List<WebElement> tableColumns;
```

The above code will return a list of WebElements for table columns. We can use the *size()* method to get the number of columns and use it in the test to perform assertions.

Having learned about *@FindBys* annotation in detail, let’s check which exception is thrown by *@FindBys* annotation if the WebElement is not found.

***Make your XML code easy to read and understand with our free online*** [***XML Prettify***](https://www.lambdatest.com/free-online-tools/xml-prettify?utm_source=devto&utm_medium=organic&utm_campaign=nov_16&utm_term=ap&utm_content=free_online_tools) ***tool. Format your code for better readability and save time.***

### Using @FindAll annotation in Selenium WebDriver

*@FindAll* annotation is used to locate a WebElement or list of WebElements with more than one selector. While using *@FindAll*, it should be noted that either of the Selectors used should be matching to locate the respective WebElement(s).

It is an “OR” condition between all the selectors given in the annotation. *@FindAll* uses multiple *@FindBy* annotation statements in the parameter to locate the WebElements.

Using *@FindAll*, we can provide two or more different selectors for a WebElement, so in case the first locator doesn’t work, it will check the second. So, eventually, the idea is to make the test less flaky by providing more than one locator for a field that doesn’t have a unique locator.

**The syntax for using *@FindAll* annotation:**

![image](https://cdn-images-1.medium.com/max/800/0*VSmtNKDx8ecrJe0C.png)

*@FindAll* can be implemented by passing on multiple *@FindBy* statements in the annotation parameter and declaring the WebElement or the list of WebElements variables. Note that the *@FindBy* statement can be supplied using either of the two implementations, as we discussed in the earlier section of this blog. If you take a look at the syntax image, you will find that both of the implementation ways are shown with two *@FindBy* statements.

### Demo: @FindAll annotation

Let’s take an example of the [Ajax Form Submit Demo](https://www.lambdatest.com/selenium-playground/ajax-form-submit-demo?utm_source=devto&utm_medium=organic&utm_campaign=nov_16&utm_term=ap&utm_content=webpage) page on LambdaTest’s Selenium Playground website.

![image](https://cdn-images-1.medium.com/max/800/0*ziXvkA-woHFQHD0a.png)

On this page, we will locate the Name field using two different locator strategies with *@FindAll* annotation.

In the DOM, we can see that two attributes can be used to locate the *Name* field, the first is using *id* — “*title*” and the second is using *name* — “*title*”.

The following lines of code will help us locate the Name field using the *@FindAll* annotation:

```plaintext

@FindAll({
@FindBy(id = "title"),
@FindBy(name = "title")
})
private WebElement nameField;
```

Using the *@FindAll* annotation id as well as name locator strategies are passed as a parameter with the *@FindBy* annotation. When the code is executed, it will first try to locate the *nameField* WebElement using the ID. If it is not found, it will try locating it using the Name strategy. The point to note here is that at least one criteria provided to locate the element should match.

Similarly, we can locate a list of WebElements using *@FindAll* annotation. The only change required will be the declaration. Instead of the WebElement, we must define it as *List&lt; WebElement &gt;*.

Here is the example code for returning a list of WebElements using *@FindAll* annotation from the [Status Codes page](https://www.lambdatest.com/selenium-playground/status-code?utm_source=devto&utm_medium=organic&utm_campaign=nov_16&utm_term=ap&utm_content=webpage) of LambdaTest’s Selenium Playground Website.

![image](https://cdn-images-1.medium.com/max/800/0*00PSNwsgF3nvW7QO.png )

The following code will help in returning the list of WebElements using *@FindAll* annotation

```plaintext
@FindAll({
@FindBy(how = How.CSS, using = "#__next > section.mt-50 > div > div > div > p"),
@FindBy(css = ".container .w-full > p")

})
private List<WebElement> statusCodeDetails;
```

We can make use of the *size()* method from the *List* interface to check for the number of elements present in the *statusCodeDetails* list.

```plaintext
public int getStatusCodes () {
return this.statusCodeDetails.size();
}
```

The *getStatusCodes()* method will return the total number of WebElements present in the *statusCodeDetails* list.

***Tired of hard-to-read SQL code? Use our fast and reliable*** [***SQL Beautifier***](https://www.lambdatest.com/free-online-tools/sql-beautifier?utm_source=devto&utm_medium=organic&utm_campaign=nov_16&utm_term=ap&utm_content=free_online_tools) ***tool to format and beautify your SQL code, making it more structured and easy to read.***

### Which Exception is thrown if the WebElement is not found?

Navigating the intricacies of web element identification in [test automation](https://www.lambdatest.com/learning-hub/automation-testing?utm_source=devto&utm_medium=organic&utm_campaign=nov_16&utm_term=ap&utm_content=learning_hub) is vital for a robust and reliable testing framework. One aspect is understanding the exceptions that may arise when using annotations like *@FindAll*, *@FindBy*, and *@FindBys*. In this section, we delve into a specific question: what happens when the WebElement is not found using these annotations?

Let’s explore the exceptions that come into play when your [automation testing](https://www.lambdatest.com/automation-testing?utm_source=devto&utm_medium=organic&utm_campaign=nov_16&utm_term=ap&utm_content=webpage) script encounters this situation and how you can handle them effectively.

### WebElement not found using @FindAll annotation

If the WebElement is not found using the *@FindAll* annotation, *NoSuchElementException* is thrown with appropriate logs.

Here is the screenshot of the *NoSuchElementException* thrown while searching for the incorrect selector for locating the *Status Codes* text on the [Status Codes Demo](https://www.lambdatest.com/selenium-playground/status-code?utm_source=devto&utm_medium=organic&utm_campaign=nov_16&utm_term=ap&utm_content=webpage) page on LambdaTest’s Selenium Playground website.

![image](https://cdn-images-1.medium.com/max/800/0*OqGe1QO0PoJ79xmN.png )

In the logs, it can be seen that Selenium WebDriver tried finding the elements by using the *@FindAll* annotation.

![image](https://cdn-images-1.medium.com/max/800/0*A-XmzxGJ0A32-aGj.png )

### WebElement not found using @FindBys annotation

*NoSuchElementException* is thrown with appropriate logs if the WebElement is not found using the *@FindBys* annotation.

Following is the screenshot of the *NoSuchElementException* thrown while searching for the incorrect selector for locating the *Male* radio button on the [Radio button demo page](https://www.lambdatest.com/selenium-playground/radiobutton-demo?utm_source=devto&utm_medium=organic&utm_campaign=nov_16&utm_term=ap&utm_content=webpage).

![image](https://cdn-images-1.medium.com/max/800/0*IyIqOtC9_X_KGlYX.png )

In the logs, it can be seen that Selenium tried finding the elements by chaining the locators provided in the *@FindBys* annotation.

![image](https://cdn-images-1.medium.com/max/800/0*FMclUd7ibqzjiGAp.png )

***Need realistic sample data? Use our free online easy-to-use*** [***Random XML***](https://www.lambdatest.com/free-online-tools/random-xml-generator?utm_source=devto&utm_medium=organic&utm_campaign=nov_16&utm_term=ap&utm_content=free_online_tools) ***Generator to create custom XML data for testing and prototyping. No sign-up required.***

### WebElement not found using @FindBy annotation

If Selenium WebDriver cannot locate a WebElement using the *@FindBy* annotation, it throws *NoSuchElementException*. It details the selector for locating the element in the log that can help us investigate the failure.

Following is the screenshot of the *NoSuchElementException* that was thrown while searching for an incorrect selector provided:

![image](https://cdn-images-1.medium.com/max/800/0*QKGj1tHcO0AQispn.png )

Let’s move towards demonstration by automating a test scenario from LambdaTest’s eCommerce website using the *@FindBy*, *@FindBys*, and *@FindAll* annotations.

### Demonstration: Locating elements and writing web automation tests

Before we get into writing the test automation scripts, let me share the details of the tools used to demo the test scenarios for using *@FindBy*, *@FindBys*, and *@FindAll* annotations.

* Programming Language — Java 17
    
* Web Automation tool — Selenium WebDriver — [*version — 4.11.0*](https://www.lambdatest.com/blog/selenium-manager-in-selenium-4-11-0/?utm_source=devto&utm_medium=organic&utm_campaign=nov_16&utm_term=ap&utm_content=blog)
    
* Application under Test — [LambdaTest’s eCommerce Playground](https://ecommerce-playground.lambdatest.io/) website
    
* Build Tool — [Maven](https://www.lambdatest.com/blog/getting-started-with-maven-for-selenium-testing?utm_source=devto&utm_medium=organic&utm_campaign=nov_16&utm_term=ap&utm_content=blog)
    
* Test Runner — [TestNG](https://www.lambdatest.com/learning-hub/testng?utm_source=devto&utm_medium=organic&utm_campaign=nov_16&utm_term=ap&utm_content=learning_hub)
    
* Cloud Platform to run web automation tests — [LambdaTest](https://www.lambdatest.com/?utm_source=devto&utm_medium=organic&utm_campaign=nov_16&utm_term=ap&utm_content=webpage)
    

**Project Setup**

Let’s create a Maven project and update the dependencies for Selenium WebDriver and TestNG in the pom.xml. Simultaneously, update the *maven-surefire* and *maven-compiler* plugins in it.

```plaintext
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<groupId>io.github.mfaisalkhatri</groupId>
<artifactId>selenium-pagefactory-demo</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>

<name>selenium-pagefactory-demo</name>
<url>http://maven.apache.org</url>

<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<selenium.java.version>4.11.0</selenium.java.version>
<testng.version>7.8.0</testng.version>
<maven.compiler.version>3.11.0</maven.compiler.version>
<surefire-version>3.1.2</surefire-version>
<java.release.version>17</java.release.version>
<maven.source.encoding>UTF-8</maven.source.encoding>
<suite-xml>testng.xml</suite-xml>
</properties>

<dependencies>

<!-- https://mvnrepository.com/artifact/org.seleniumhq.selenium/selenium-java -->
<dependency>
<groupId>org.seleniumhq.selenium</groupId>
<artifactId>selenium-java</artifactId>
<version>${selenium.java.version}</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.testng/testng -->
<dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
<version>${testng.version}</version>
<scope>test</scope>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>${maven.compiler.version}</version>
<configuration>
<release>${java.release.version}</release>
<encoding>${maven.source.encoding}</encoding>
<forceJavacCompilerUse>true</forceJavacCompilerUse>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>${surefire-version}</version>
<executions>
<execution>
<goals>
<goal>test</goal>
</goals>
</execution>
</executions>
<configuration>
<useSystemClassLoader>false</useSystemClassLoader>
<properties>
<property>
<name>usedefaultlisteners</name>
<value>false</value>
</property>
</properties>
<suiteXmlFiles>
<suiteXmlFile>${suite-xml}</suiteXmlFile>
</suiteXmlFiles>
<argLine>${argLine}</argLine>
</configuration>
</plugin>
</plugins>
</build>
</project>
```

**Test Scenario**

1. Navigate to the [Login Page](https://ecommerce-playground.lambdatest.io/index.php?route=account/login) of [LambdaTest’s eCommerce Playground](https://ecommerce-playground.lambdatest.io/) website.
    
2. Locate the E-Mail Address field using the *@FindBys* annotation.
    
3. Locate the Password field and Login using the *@FindBy* annotation.
    
4. Enter a valid email address and password in the respective fields.
    
5. Click on the Login button.
    
6. On successful login, verify that the My Account Page is displayed.
    
7. Locate the Logout link using *@FindAll* annotation and click on it to logout.
    

**The login screen of LambdaTest’s eCommerce Website**

![image](https://cdn-images-1.medium.com/max/800/0*IT5lqfwc0fuVu7eC.png )

**Page displayed after successful Login with the Logout link**

![image](https://cdn-images-1.medium.com/max/800/0*CjHZ0RnTxM_feW88.png )

**Implementation**  
Before we begin writing the code, let’s first find the selectors for the fields on the Login Page. The first field is the E-Mail Address field. Let’s check out the DOM of the web page to find the selectors for this field.

![image](https://cdn-images-1.medium.com/max/800/0*d0ANuk9egWD4dJdw.png )

All the selectors for the Login Page fields will be declared in the *LoginPage* class.

We can see that the parent locator for this field is className = “card-body” and *name* attribute — “email” as well as *ID* attribute “input-email” can be used as the target locator. We will be using the *@FindBys* annotation to locate this field.

***Need YAML data for testing? No need to stress about YAML data — Try our*** [***Random YAML***](https://www.lambdatest.com/free-online-tools/random-yaml-generator?utm_source=devto&utm_medium=organic&utm_campaign=nov_16&utm_term=ap&utm_content=free_online_tools) ***Generator for a quick and effortless way to generate YAML data in seconds.***

Below is the WebElement variable *emailAddressField* declared inside the *LoginPage* class.

```plaintext
@FindBys({
@FindBy(how = How.CLASS_NAME, using = "card-body"),
@FindBy(how = How.NAME, using = "email")
})
private WebElement emailAddressField;
```

Next, let’s locate the Password field.

![image](https://cdn-images-1.medium.com/max/800/0*LCbAq5QF5VTbuUND.png )

In the DOM, it can be seen that locator strategies like ID — “input-password” and NAME — “password” can be used to locate the Password field.

Below is the WebElement variable *passwordField* declared inside the *LoginPage* class that will help locate the Password field.

```plaintext
@FindBy(how = How.NAME, using = "password" )
private WebElement passwordField;
```

Next, let’s locate the Login button.

![image](https://cdn-images-1.medium.com/max/800/0*DCv4NufNz2YHw4wg.png )

It is pretty simple to locate the Login button, we can make use of CSS Selector — “input\[type=”submit”\].

Below is the WebElement variable *loginBtn* declared inside the *LoginPage* class to locate the Login button using the *@FindBy* annotation.

```plaintext
@FindBy (css = "input[type=\"submit\"]")
private WebElement loginBtn;
```

Let’s now create a new method named *performLogin()* inside the *LoginPage* class, that will perform the interaction with the E-Mail Address, Password fields, and Login button by entering the respective credentials in the fields to login in the website.

```plaintext
public MyAccountPage performLogin(final String emailAddress, final String password) {
this.emailAddressField.sendKeys(emailAddress);
this.passwordField.sendKeys(password);
this.loginBtn.click();

return new MyAccountPage(this.driver);
}
```

The *performLogin()* method consumes two parameters called *emailAddress* and *Password* that will be supplied from the test to perform login, and it will return a new instance of the *MyAccountPage* class. The new instance of the *MyAccountPage* class is returned from this method as, after success, the website will take us to the My Account Page.

We have declared the WebElement variables and a method to perform login. Now, it is time to use the *initElements()* method from the *PageFactory* class of Selenium so the annotations work seamlessly. We will call the *initElement()* method in the *Constructor* of the *LoginPage* class.

```plaintext
private final WebDriver driver;
public LoginPage(final WebDriver driver) {
this.driver = driver;
PageFactory.initElements(driver, this);
}
```

Below is the final version of the *LoginPage* class after all the variables, methods, and constructors are added to it.

```plaintext
public class LoginPage {
private final WebDriver driver;
public LoginPage(final WebDriver driver) {
this.driver = driver;
PageFactory.initElements(driver, this);
}

@FindBys({
@FindBy(how = How.CLASS_NAME, using = "card-body"),
@FindBy(how = How.NAME, using = "email")
})
private WebElement emailAddressField;
@FindBy(how = How.NAME, using = "password" )
private WebElement passwordField;

@FindBy (css = "input[type=\"submit\"]")
private WebElement loginBtn;

public MyAccountPage performLogin(final String emailAddress, final String password) {
this.emailAddressField.sendKeys(emailAddress);
this.passwordField.sendKeys(password);
this.loginBtn.click();

return new MyAccountPage(this.driver);
}
}
```

Once the login step is complete, the My Account page will be displayed. To verify this step, let’s locate the title of the page that is “My Account”.

![image](https://cdn-images-1.medium.com/max/800/0*phpeJE8VYTvEKuTv.png )

We can make use of the CSS Selector — “#content &gt; div:nth-child(1) h2” to locate the title — “My Account”.

The following *myAccountTitle* variable declared inside the *MyAccountPage* class will locate the title using the *@FindBy* annotation.

```plaintext
@FindBy(css = "#content > div:nth-child(1) h2")
@CacheLookup
private WebElement myAccountTitle;
```

*@CacheLookup* annotation is used for this variable to store this WebElement in the cache so when we run the test next time, it will take the WebElement from the cache instead of searching for it on the web page. This will help in saving the execution time.

```plaintext
public String getMyAccountPageTitle() {
return this.myAccountTitle.getText();
}
```

The *getMyAccountPageTitle()* will return the text from the WebElement *myAccountTitle* in String format that will help us in performing assertion for the title in the test.

***Need a quick placeholder image for your project? Use our online*** [***placeholder image***](https://www.lambdatest.com/free-online-tools/placeholder-image-generator?utm_source=devto&utm_medium=organic&utm_campaign=nov_16&utm_term=ap&utm_content=free_online_tools) ***generator tool that helps you generate customizable images for your projects.***

The next step that we need to take care of is to locate the Logout link that will be displayed after the successful login on the My Account page.

![image](https://cdn-images-1.medium.com/max/800/0*az04qx1cO1u5NwGt.png )

There are two ways to locate this Logout link, one is using the LinkText locator strategy, and the second is using the CSS Selector locator strategy where we use the Selector — “*#column-right &gt; div &gt; a:nth-child(14)*”. Let’s use the *@FindAll* annotation and add these locator strategies to locate the link.

Below is the WebElement variable *logoutLink* declared to locate the link using *@FindAll* annotation.

```plaintext
@FindAll({
@FindBy(how = How.LINK_TEXT, using = "Logout"),
@FindBy(how = How.CSS, using = "#column-right > div > a:nth-child(14)")
})
private WebElement logoutLink;
```

A new method name *logoutFromECommercePlayground()* is created in the MyAccount Page that will interact with the logout link by performing a click on it.

```plaintext
public void logoutFromEcommercePlayground() {
this.logoutLink.click();
}
```

We will be calling the *initElements()* method from the *PageFactory* class in the constructor of the *MyAccountPage* class like we did in the *LoginPage* class.

```plaintext
public class MyAccountPage {

public MyAccountPage(final WebDriver driver) {
PageFactory.initElements(driver, this);
}

@FindBy(css = "#content > div:nth-child(1) h2")
@CacheLookup
private WebElement myAccountTitle;

public String getMyAccountPageTitle() {
return this.myAccountTitle.getText();
}

@FindAll({
@FindBy(how = How.LINK_TEXT, using = "Logout"),
@FindBy(how = How.CSS, using = "#column-right > div > a:nth-child(14)")
})
private WebElement logoutLink;

public void logoutFromEcommercePlayground() {
this.logoutLink.click();
}
}
```

With the implementation of the Logout step, we have completed the part of writing the Page objects. Now, let’s write the test.

```plaintext
public class LambdaTestEcommercePlaygroundTests extends BaseTest {

@Test
public void testLogin() {
this.driverManager.getDriver().get("https://ecommerce-playground.lambdatest.io/index.php?route=account/login");
final LoginPage loginPage = new LoginPage(this.driverManager.getDriver());
final MyAccountPage myAccountPage = loginPage.performLogin("john+2@gmail.com", "Pass@1234");

assertEquals(myAccountPage.getMyAccountPageTitle(), "My Account");
myAccountPage.logoutFromEcommercePlayground();

}
}
```

The first statement in the *testLogin()* method navigates to the LambdaTest eCommerce website. Next, the *LoginPage* class is instantiated, where we pass on the WebDriver instance using the *drivermanager* class’ *getDriver()* method.

The *LambdaTestPlayGroundTests* class is extended by the *BaseTest* class that takes care of the *WebDriver* initialization and teardown using the *DriverManager* class.

```plaintext
public class BaseTest {

protected DriverManager driverManager;

@BeforeMethod(alwaysRun = true)
public void testSetup() {
this.driverManager = new DriverManager();
this.driverManager.createChromeDriverInCloud();
}

@AfterMethod(alwaysRun = true)
public void tearDown() {
this.driverManager.quitDriver();
}
}
```

The *DriverManager* class takes care of all the setup and configuration of the WebDriver interface. As we must run the tests in the LambdaTest cloud grid, the *createChromeDriverInCloud()* method from the *DriverManager* class will help us start a session.

LambdaTest is an AI-powered test orchestration and execution platform for [Selenium testing](https://www.lambdatest.com/selenium-automation?utm_source=devto&utm_medium=organic&utm_campaign=nov_16&utm_term=ap&utm_content=webpage) with Java that offers [automated testing](https://www.lambdatest.com/learning-hub/automation-testing?utm_source=devto&utm_medium=organic&utm_campaign=nov_16&utm_term=ap&utm_content=blog) on many browsers and operating systems. It enables parallel testing, local testing, and integration with popular [Java testing frameworks](https://www.lambdatest.com/blog/best-java-testing-frameworks/?utm_source=devto&utm_medium=organic&utm_campaign=nov_16&utm_term=ap&utm_content=blog) like TestNG, JUnit, etc.

It supports [online Selenium Grid](https://www.lambdatest.com/selenium-grid-online?utm_source=devto&utm_medium=organic&utm_campaign=nov_16&utm_term=ap&utm_content=webpage) for scalable testing and provides features such as automated screenshot capture, video recording, network simulation, and seamless integration with CI/CD pipelines, making it a comprehensive platform for developers to ensure their web applications function reliably across diverse environments.

***Tired of messy, unorganized XML code? Our*** [***XML Stringify***](https://www.lambdatest.com/free-online-tools/xml-stringify?utm_source=devto&utm_medium=organic&utm_campaign=nov_16&utm_term=ap&utm_content=free_online_tools) ***tool makes formatting a breeze, allowing you to quickly and easily convert your code into a readable string***

You can also have a detailed look at this tutorial to start automation testing on LambdaTest.

{% youtube 86LQsMtBs5k %}


Subscribe to the [LambdaTest YouTube Channel](https://www.youtube.com/c/LambdaTest?sub_confirmation=1?utm_source=devto&utm_medium=organic&utm_campaign=nov_16&utm_term=ap&utm_content=video) for software testing tutorials around [Selenium](https://www.lambdatest.com/selenium?utm_source=devto&utm_medium=organic&utm_campaign=nov_16&utm_term=ap&utm_content=webpage), [Playwright testing](https://www.lambdatest.com/playwright-testing?utm_source=devto&utm_medium=organic&utm_campaign=nov_16&utm_term=ap&utm_content=webpage), and more.

```plaintext
public class DriverManager {

protected WebDriver driver;
private static final String LT_USERNAME = System.getProperty("LT_USERNAME");
private static final String LT_ACCESS_KEY = System.getProperty("LT_ACCESS_KEY");
private static final String GRID_URL = "@hub.lambdatest.com/wd/hub";

public void createChromeDriverInCloud() {
final ChromeOptions browserOptions = new ChromeOptions();
browserOptions.setPlatformName("Windows 10");
browserOptions.setBrowserVersion("114.0");
final HashMap<String, Object> ltOptions = new HashMap<>();
ltOptions.put("username", LT_USERNAME);
ltOptions.put("accessKey", LT_ACCESS_KEY);
ltOptions.put("resolution", "2560x1440");
ltOptions.put("selenium_version", "4.0.0");
ltOptions.put("build", "LambdaTest ECommerce Playground Build");
ltOptions.put("name", "LambdaTest ECommerce Playground Tests");
ltOptions.put("w3c", true);
ltOptions.put("plugin", "java-testNG");
browserOptions.setCapability("LT:Options", ltOptions);
try {
this.driver = new RemoteWebDriver(new URL(format("https://{0}:{1}{2}", LT_USERNAME, LT_ACCESS_KEY, GRID_URL)), browserOptions);
} catch (final MalformedURLException e) {
throw new Error("RemoteDriver's URL not set correctly!", e);
}
this.driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(10));
}

public WebDriver getDriver() {
return this.driver;
}

public void quitDriver() {
this.driver.quit();
}
}
```

![image](https://cdn-images-1.medium.com/max/800/0*dnrgRCmtSLSPYWDF.png )

These desired capabilities in the *createChromeDriverInCloud()* method can be easily copied using [LambdaTest’s Capabilities Generator](https://www.lambdatest.com/capabilities-generator/?utm_source=devto&utm_medium=organic&utm_campaign=nov_16&utm_term=ap&utm_content=webpage).

![image](https://cdn-images-1.medium.com/max/800/0*Ext8f2J2heSaJIN9.png )

Coming back to the tests, next, we need to perform the login by calling the *performLogin()* method from the [*LoginPage*](https://github.com/mfaisalkhatri/selenium-pagefactory-demo/blob/main/src/test/java/io/github/mfaisalkhatri/pages/lambdatestecommerce/LoginPage.java) class and passing on the email address and password in the respective fields.

![image](https://cdn-images-1.medium.com/max/800/0*y-VGzBgAPeZbZlEV.png )

The *performLogin()* method already returns a new instance of the *MyAccountPage* class; we don’t need to instantiate the *MyAccountPage* class in the test method explicitly.

![image](https://cdn-images-1.medium.com/max/800/0*TXdI8SxuDzPxKRsx.png )

After successful login, the next statement of code will perform the assertion to check that the My Account page title is “My Account”. Finally, it will locate the Logout link and click on it. With this, the test scenario is marked complete.

**Test Execution**

The following testng.xml file is created that will help us in executing the test using TestNG.

```plaintext
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd">
<suite name="LambdaTest ecommerce playground website test suite ">
<test name="Page Factory demo tests using LambdaTest ECommerce Playground">
<classes>
<class name="io.github.mfaisalkhatri.tests.LambdaTestEcommercePlaygroundTests">
<methods>
<include name="testLogin"/>
</methods>
</class>
</classes>
</test> <!-- Test -->
</suite> <!-- Suite -->
```

As we need to execute the tests on LambdaTest Cloud grid we need to provide the following secret values in the *Edit Configuration* window in the IDE as follows. These values can be found in the *Password & Security* section after logging in to the LambdaTest website.

* \-DLT\_USERNAME=&lt; LambdaTest Username &gt;
    
* \-DLT\_ACCESS\_KEY=&lt; LambdaTest Access Key &gt;
    

To execute the tests, right click on the testng.xml file and select the option “*Run ‘…/testng.xml*’”.

***Simplify your data export process with our efficient*** [***SQL to CSV***](https://www.lambdatest.com/free-online-tools/sql-to-csv?utm_source=devto&utm_medium=organic&utm_campaign=nov_16&utm_term=ap&utm_content=free_online_tools) ***Converter. Convert SQL queries to CSV format seamlessly and unlock data versatility. Try it now.***

Below is the screenshot of the tests run from IntelliJ.

![image](https://cdn-images-1.medium.com/max/800/0*laAh1RRpXAyAkY0O.png )

We can check the test execution details in the [LambdaTest Dashboard](https://accounts.lambdatest.com/dashboard), which shows the granular details of the test, like OS, browser name, version, and logs.

![image](https://cdn-images-1.medium.com/max/800/0*YInoog8LZKNejUra.png)

### Food for Thought 💡

As per my experience, the Page Object Model offers more leverage than the Page Factory Model. In Page Factory, we can only define a WebElement or List &lt; WebElements &gt;; hence, it restricts the usage of the *By* class of Selenium WebDriver. This can restrict us from using different Expected Conditions while using Explicit Wait.

***Get faster loading times and better user experience with our efficient*** [***JSON Stringify***](https://www.lambdatest.com/free-online-tools/json-stringify?utm_source=devto&utm_medium=organic&utm_campaign=nov_16&utm_term=ap&utm_content=free_online_tools) ***tool. Quickly compress your JSON data with ease and optimize your website now.***

### Final Thoughts

*@FindBy*, *@FindBys*, and *@FindAll* annotations can be used to implement Page Factory Model in Selenium WebDriver. These are quite neat and clear annotations as we don’t have to use the findElement/findElements method to search for a webelement. Also, we can use the *@CacheLookup* annotation, which allows us to save the WebElement in the cache so that when the tests run next time, the WebElement is fetched from the cache, thus saving time. However, we need to call the *intiElements()* method from the *PageFactory* class in every page object class; otherwise, these annotations won’t work.

The Page Factory Model has advantages and disadvantages; we can check out what suits us the best and move forward to use the model in the automation suite.