Finding Elements From HTML Documents

After we have finished this lesson, we

  • Know how we can find elements from HTML documents.
  • Understand how we can implement testable user interfaces.
  • Know how we can write faster and more robust end-to-end tests.

Introduction to System Under Test

During this lesson we will write end-to-end tests that find elements from an HTML document. The source code of our HTML document looks as follows:

 1<!DOCTYPE html>
 2<html>
 3<head>
 4    <title>Hello World!</title>
 5</head>
 6<body>
 7    <h1 id="page-header">Hello World!</h1>
 8
 9    <p class="text-block">Lorem ipsum</p>
10
11    <blockquote>This is a quote.</blockquote>
12
13    <input name="text-input" value="Random text"/>
14
15    <ul>
16        <li><a href="http://www.google.com">Go to Google</a></li>
17        <li>
18            <a href="https://www.testwithspring.com">
19                Go to Test With Spring Course
20            </a>
21        </li>
22    </ul>
23</body>
24</html>

Next, we will find out how we can find elements from HTML documents.

Finding Elements From HTML Documents

The SearchContext interface declares two methods that we can use when we want to find elements from HTML documents. These methods are:

  • The findElement(By by) method returns the first element that matches with the given search criteria.
  • The findElements(By by) method returns a list of elements that match with the given search criteria.

Both of these methods take a By object as a method parameter. This object specifies the search criteria that is used to find the requested element or elements. We can create these objects by using the static factory methods of the By class.

Also, before we can use these methods, we have to understand these two things:

  • These methods return elements that are found from the current context. This means that if we invoke these methods by using a WebDriver object, our code searches the requested elements from the entire HTML document. On the other hand, if we invoke these methods by using a WebElement object, our code searches the requested elements from the children of the current HTML element.
  • If we want to ensure that an element is not found from the HTML document, we shouldn’t use the findElement() method because it throws a NoSuchElementException if no element is found. Instead, we should invoke the findElements() method and verify that the size of the returned list is zero.

Next, we will find out how we can find elements from our HTML document. Let’s start by finding an element that has a specific tag name.

Finding an Element With a Tag Name

If we want to find an element that has a specific tag name, we can create the required search criteria by invoking the static tagName() method of the By class. This method takes the name of the HTML tag as a method parameter.

After we have written the required end-to-end test, the relevant part of our test class looks as follows:

 1import org.junit.Before;
 2import org.junit.Test;
 3import org.junit.experimental.categories.Category;
 4import org.junit.runner.RunWith;
 5import org.openqa.selenium.By;
 6import org.openqa.selenium.WebDriver;
 7import org.openqa.selenium.WebElement;
 8import org.openqa.selenium.chrome.ChromeDriver;
 9
10import static org.assertj.core.api.Assertions.assertThat;
11
12@RunWith(SeleniumTestRunner.class)
13@SeleniumTest(driver = ChromeDriver.class)
14@Category(EndToEndTest.class)
15public class RenderExamplePageTest {
16
17    @SeleniumWebDriver
18    private WebDriver browser;
19
20    @Before
21    public void openExamplePage() {
22        browser.get("http://localhost:8080");
23    }
24
25    @Test
26    public void shouldOpenPageWithCorrectQuoteOne() {
27        WebElement quote = browser.findElement(By.tagName("blockquote"));
28        assertThat(quote.getText()).isEqualTo("This is a quote.");
29    }
30}

Let’s move on and find out how we can find an element that has a specific id.

Finding an Element With an Id

If we want to find an element that has a specific id, we can create the required search criteria by invoking the static id() method of the By class. This method takes the id of the requested element as a method parameter.

After we have written the required end-to-end test, the relevant part of our test class looks as follows:

 1import org.junit.Before;
 2import org.junit.Test;
 3import org.junit.experimental.categories.Category;
 4import org.junit.runner.RunWith;
 5import org.openqa.selenium.By;
 6import org.openqa.selenium.WebDriver;
 7import org.openqa.selenium.WebElement;
 8import org.openqa.selenium.chrome.ChromeDriver;
 9
10import static org.assertj.core.api.Assertions.assertThat;
11
12@RunWith(SeleniumTestRunner.class)
13@SeleniumTest(driver = ChromeDriver.class)
14@Category(EndToEndTest.class)
15public class RenderExamplePageTest {
16
17    @SeleniumWebDriver
18    private WebDriver browser;
19
20    @Before
21    public void openExamplePage() {
22        browser.get("http://localhost:8080");
23    }
24
25    @Test
26    public void shouldOpenPageWithCorrectHeader() {
27        WebElement pageHeader = browser.findElement(By.id("page-header"));
28        assertThat(pageHeader.getText()).isEqualTo("Hello World!");
29    }
30}

Let’s move on and find out how we can find an element by using a class name as search criteria.

Finding an Element With a Class Name

If we want to find an element that has a specific class name, we can create the required search criteria by invoking the static className() method of the By class. This method takes the class name of the requested element as a method parameter.

After we have written the required end-to-end test, the relevant part of our test class looks as follows:

 1import org.junit.Before;
 2import org.junit.Test;
 3import org.junit.experimental.categories.Category;
 4import org.junit.runner.RunWith;
 5import org.openqa.selenium.By;
 6import org.openqa.selenium.WebDriver;
 7import org.openqa.selenium.WebElement;
 8import org.openqa.selenium.chrome.ChromeDriver;
 9
10import static org.assertj.core.api.Assertions.assertThat;
11
12@RunWith(SeleniumTestRunner.class)
13@SeleniumTest(driver = ChromeDriver.class)
14@Category(EndToEndTest.class)
15public class RenderExamplePageTest {
16
17    @SeleniumWebDriver
18    private WebDriver browser;
19
20    @Before
21    public void openExamplePage() {
22        browser.get("http://localhost:8080");
23    }
24
25    @Test
26    public void shouldOpenPageWithCorrectTextBlockOne() {
27        WebElement textBlock = browser.findElement(
28                By.className("text-block")
29        );
30        assertThat(textBlock.getText()).isEqualTo("Lorem ipsum");
31    }
32}

Next, we will find out how we can use the name of the element as search criteria.

Finding an Element With a Name

If we want to find an element whose name attribute has a specific value, we can create the required search criteria by invoking the static name() method of the By class. This method takes the value of the name attribute as a method parameter.

After we have written the required end-to-end test, the relevant part of our test class looks as follows:

 1import org.junit.Before;
 2import org.junit.Test;
 3import org.junit.experimental.categories.Category;
 4import org.junit.runner.RunWith;
 5import org.openqa.selenium.By;
 6import org.openqa.selenium.WebDriver;
 7import org.openqa.selenium.WebElement;
 8import org.openqa.selenium.chrome.ChromeDriver;
 9
10import static org.assertj.core.api.Assertions.assertThat;
11
12@RunWith(SeleniumTestRunner.class)
13@SeleniumTest(driver = ChromeDriver.class)
14@Category(EndToEndTest.class)
15public class RenderExamplePageTest {
16
17    @SeleniumWebDriver
18    private WebDriver browser;
19
20    @Before
21    public void openExamplePage() {
22        browser.get("http://localhost:8080");
23    }
24
25    @Test
26    public void shouldOpenWithTextInputThatHasCorrectText() {
27        WebElement textInput = browser.findElement(By.name("text-input"));
28        assertThat(textInput.getAttribute("value")).isEqualTo("Random text");
29    }
30}

Let’s move on and find out how we can find a link element that has a specific link text.

If we want to find a link element that has a specific link text, we can create the required search criteria by invoking the static linkText() method of the By class. This method takes the expected link text as a method parameter.

After we have written the required end-to-end test, the relevant part of our test class looks as follows:

 1import org.junit.Before;
 2import org.junit.Test;
 3import org.junit.experimental.categories.Category;
 4import org.junit.runner.RunWith;
 5import org.openqa.selenium.By;
 6import org.openqa.selenium.WebDriver;
 7import org.openqa.selenium.WebElement;
 8import org.openqa.selenium.chrome.ChromeDriver;
 9
10import static org.assertj.core.api.Assertions.assertThat;
11
12@RunWith(SeleniumTestRunner.class)
13@SeleniumTest(driver = ChromeDriver.class)
14@Category(EndToEndTest.class)
15public class RenderExamplePageTest {
16
17    @SeleniumWebDriver
18    private WebDriver browser;
19
20    @Before
21    public void openExamplePage() {
22        browser.get("http://localhost:8080");
23    }
24
25    @Test
26    public void shouldOpenPageThatHasLinkToGoogle() {
27        WebElement googleLink = browser.findElement(
28                By.linkText("Go to Google")
29        );
30        assertThat(googleLink.getAttribute("href"))
31                .isEqualTo("http://www.google.com/");
32    }
33}

Next, we will find out how we can find an element by using a partial link text as search criteria.

If we want to find a link element whose link text contains a specific string, we can create the required search criteria by invoking the static partialLinkText() method of the By class. This method takes the partial link text as a method parameter.

After we have written the required end-to-end test, the relevant part of our test class looks as follows:

 1import org.junit.Before;
 2import org.junit.Test;
 3import org.junit.experimental.categories.Category;
 4import org.junit.runner.RunWith;
 5import org.openqa.selenium.By;
 6import org.openqa.selenium.WebDriver;
 7import org.openqa.selenium.WebElement;
 8import org.openqa.selenium.chrome.ChromeDriver;
 9
10import static org.assertj.core.api.Assertions.assertThat;
11
12@RunWith(SeleniumTestRunner.class)
13@SeleniumTest(driver = ChromeDriver.class)
14@Category(EndToEndTest.class)
15public class RenderExamplePageTest {
16
17    @SeleniumWebDriver
18    private WebDriver browser;
19
20    @Before
21    public void openExamplePage() {
22        browser.get("http://localhost:8080");
23    }
24
25    @Test
26    public void shouldOpenPageThatHasLinkToTestWithSpringCourse() {
27        WebElement testWithSpringLink = browser.findElement(
28                By.partialLinkText("Test With Spring")
29        );
30        assertThat(testWithSpringLink.getAttribute("href"))
31                .isEqualTo("https://www.testwithspring.com/");
32    }
33}

Let’s move on and find out how we can find an element by using a CSS selector as search criteria.

Finding an Element With a CSS Selector

Even though the “basic” methods provided by the By class are suitable for most situations, sometimes we need extra flexibility. If this is the case, we can specify our search criteria by using a CSS selector.

If we want to find an element by using a CSS selector, we can create the required search criteria by invoking the static cssSelector() method of the By class. This method takes the CSS selector as a method parameter.

After we have written the required end-to-end test that finds the element by using the class: text-block, the relevant part of our test class looks as follows:

 1import org.junit.Before;
 2import org.junit.Test;
 3import org.junit.experimental.categories.Category;
 4import org.junit.runner.RunWith;
 5import org.openqa.selenium.By;
 6import org.openqa.selenium.WebDriver;
 7import org.openqa.selenium.WebElement;
 8import org.openqa.selenium.chrome.ChromeDriver;
 9
10import static org.assertj.core.api.Assertions.assertThat;
11
12@RunWith(SeleniumTestRunner.class)
13@SeleniumTest(driver = ChromeDriver.class)
14@Category(EndToEndTest.class)
15public class RenderExamplePageTest {
16
17    @SeleniumWebDriver
18    private WebDriver browser;
19
20    @Before
21    public void openExamplePage() {
22        browser.get("http://localhost:8080");
23    }
24
25    @Test
26    public void shouldOpenPageWithCorrectTextBlockTwo() {
27        WebElement textBlock = browser.findElement(
28                By.cssSelector(".text-block")
29        );
30        assertThat(textBlock.getText()).isEqualTo("Lorem ipsum");
31    }
32}

Next, we will find out how we can specify our search criteria by using an XPath expression.

Finding an Element With an XPath Expression

If the “basic” methods provided by the By class are not good enough and we cannot (or don’t want to) use a CSS selector, we can also specify our search criteria by using an XPath expression.

If we want to find an element by using an XPath expression, we can create the required search criteria by invoking the static xpath() method of the By class. This method takes the XPath expression as a method parameter.

After we have written the required end-to-end test that finds the element by using the XPath expression: /html/body/blockquote, the relevant part of our test class looks as follows:

 1import org.junit.Before;
 2import org.junit.Test;
 3import org.junit.experimental.categories.Category;
 4import org.junit.runner.RunWith;
 5import org.openqa.selenium.By;
 6import org.openqa.selenium.WebDriver;
 7import org.openqa.selenium.WebElement;
 8import org.openqa.selenium.chrome.ChromeDriver;
 9
10import static org.assertj.core.api.Assertions.assertThat;
11
12@RunWith(SeleniumTestRunner.class)
13@SeleniumTest(driver = ChromeDriver.class)
14@Category(EndToEndTest.class)
15public class RenderExamplePageTest {
16
17    @SeleniumWebDriver
18    private WebDriver browser;
19
20    @Before
21    public void openExamplePage() {
22        browser.get("http://localhost:8080");
23    }
24
25    @Test
26    public void shouldOpenPageWithCorrectQuoteTwo() {
27        WebElement quote = browser.findElement(
28                By.xpath("/html/body/blockquote")
29        );
30        assertThat(quote.getText()).isEqualTo("This is a quote.");
31    }
32}

We can now find elements by using the static methods provided by the By class. Let’s move on and take a look at “best practices” that help us to implement testable user interfaces and write better end-to-end tests.

“Best Practices”

There are two principles that help us to implement testable user interfaces, and write faster and more robust end-to-end tests. These principles are:

First, We have to assign proper ids, classes, and names to our HTML elements. This allows us to find these elements by using search criteria that is less likely to break when we make changes to our user interface.

Second, when we find elements from HTML documents, we should use either the id, name, or class of the requested element because of these four reasons:

  • Using element’s id or name is the fastest way (in terms of performance) to find elements from HTML documents.
  • End-to-end tests that use either the id, class, or name of the requested element are less likely to break when we make changes to our user interface.
  • If our user interface has to support multiple languages or we do a lot A/B testing, we shouldn’t use the link text as search criteria because keeping these locators up-to-date takes a lot of work.
  • The locators that use either CSS selectors or XPath expressions are often brittle and will break our end-to-end tests when we make changes to our user interface. However, if we have no other choice, we can naturally use these techniques as well.

Let’s summarize what we learned from this lesson.

Summary

This lesson has taught us four things:

  • We can find elements from HTML documents by using the methods declared by the SearchContext interface.
  • We can specify the search criteria that is used to find elements from HTML documents by using the static methods provided by the By class.
  • We should assign proper ids, classes, and names to our HTML elements because this helps us to write faster and more robust end-to-end tests.
  • When we find elements from HTML documents, we should use either the id, name, or class of the requested element because this helps us to write faster and less brittle end-to-end tests.

Get the source code from Github