How To Work with HTML Data Tables in Selenium Java, Python, C#, Ruby

Yosuva ArulanthuC#, Java, Python, Ruby, Selenium, Test Automation, TestingLeave a Comment

Odds are at some point you’ve come across the use of tables in a web application to display data or information to a user, giving them the option to sort and manipulate it. Depending on your application it can be quite common and something you will want to write an automated test for.

But when the table has no helpful, semantic markup (e.g. easy to use id or class attributes) it quickly becomes more difficult to work with and write tests against it. And if you’re able to pull something together, it will likely not work against older browsers.

Solution

You can easily traverse a table through the use of CSS Pseudo-classes.

A quick primer on Tables and CSS Pseudo-classes

Understanding the broad strokes of an HTML table’s structure goes a long way in writing effective automation against it. So here’s a quick primer.

A table has…

  • a header (e.g. <thead>)
  • a body (e.g. <tbody>).
  • rows (e.g. <tr>) — horizontal slats of data
  • columns — vertical slats of data

Columns are made up of cells which are…

  • a header (e.g., <th>)
  • one or more standard cells (e.g., <td> — which is short for table data)

CSS Pseudo-classes work by walking through the structure of an object and targeting a specific part of it based on a relative number (e.g. the third <td> cell from a row in the table body). This works well with tables since we can grab all instances of a target (e.g. the third <td> cell from each <tr> in the table body) and use it in our test — which would give us all of the data for the third column.

Let’s step through some examples for a common set of table functionality like sorting columns in ascending and descending order.

Example

NOTE: You can see the application under test here. It’s an example from the-internet.

Java

// filename: Tables.java
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.firefox.FirefoxDriver;
import java.util.LinkedList;
import java.util.List;
import static org.hamcrest.CoreMatchers.*;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.number.OrderingComparison.lessThan;
import static org.hamcrest.number.OrderingComparison.lessThanOrEqualTo;
import static org.hamcrest.number.OrderingComparison.greaterThanOrEqualTo;

public class Tables {
    WebDriver driver;

    @Before
    public void setUp() throws Exception {
        driver = new FirefoxDriver();

    }

    @After
    public void tearDown() throws Exception {
        driver.quit();
    }
    
    @Test
    public void withoutHelpfulMarkupDuesAscending() {
    {
        driver.get("http://the-internet.herokuapp.com/tables");
        driver.findElement(By.cssSelector("#table1 thead tr th:nth-of-type(4)")).click();

        List<WebElement> dues = driver.findElements(By.cssSelector("#table1 tbody tr td:nth-of-type(4)"));
        List<Double> dueValues = new LinkedList<Double>();
        for(WebElement element : dues){
            dueValues.add(Double.parseDouble(element.getText().replace("$", "")));
        }

        for(int counter = 0; counter < dueValues.size() - 1; counter++){
            assertThat(dueValues.get(counter), is(lessThanOrEqualTo(dueValues.get(counter + 1))));
        }
    }

    @Test
    public void withoutHelpfulMarkupDuesDescending() {
        driver.get("http://the-internet.herokuapp.com/tables");

        driver.findElement(By.cssSelector("#table1 thead tr th:nth-of-type(4)")).click();
        driver.findElement(By.cssSelector("#table1 thead tr th:nth-of-type(4)")).click();

        List<WebElement> dues = driver.findElements(By.cssSelector("#table1 tbody tr td:nth-of-type(4)"));
        List<Double> dueValues = new LinkedList<Double>();
        for (WebElement element : dues) {
            dueValues.add(Double.parseDouble(element.getText().replace("$", "")));
        }

        for (int counter = 0; counter < dueValues.size() - 1; counter++) {
            assertThat(dueValues.get(counter), is(greaterThanOrEqualTo(dueValues.get(counter + 1))));
        }
    }

    @Test
    public void withoutHelpfulMarkupEmailAscending() {
        driver.get("http://the-internet.herokuapp.com/tables");

        driver.findElement(By.cssSelector("#table1 thead tr th:nth-of-type(3)")).click();

        List<WebElement> emails = driver.findElements(By.cssSelector("#table1 tbody tr td:nth-of-type(3)"));
        for(int counter = 0; counter < emails.size() -1; counter++){
            assertThat(
                    emails.get(counter).getText().compareTo(emails.get(counter + 1).getText()),
                    is(lessThan(0)));
        }
    }

}

Python

# filename: tables.py
import unittest
from selenium import webdriver


class Tables(unittest.TestCase):

    def setUp(self):
        self.driver = webdriver.Firefox()

    def tearDown(self):
        self.driver.quit()

    def test_sort_number_column_in_ascending_order_with_limited_locators(self):
        driver = self.driver
        driver.get('http://the-internet.herokuapp.com/tables')
        driver.find_element_by_css_selector('#table1 thead tr th:nth-of-type(4)').click()
        due_column = driver.find_elements_by_css_selector('#table1 tbody tr td:nth-of-type(4)')
        dues = [float(due.text.replace('$','')) for due in due_column]
        assert dues == sorted(dues)

    def test_sort_number_column_in_descending_order_with_limited_locators(self):
        driver = self.driver
        driver.get('http://the-internet.herokuapp.com/tables')
        driver.find_element_by_css_selector('#table1 thead tr th:nth-of-type(4)').click()
        driver.find_element_by_css_selector('#table1 thead tr th:nth-of-type(4)').click()
        due_column = driver.find_elements_by_css_selector('#table1 tbody tr td:nth-of-type(4)')
        dues = [float(due.text.replace('$','')) for due in due_column]
        assert dues == sorted(dues, reverse=True)

    def test_sort_text_column_in_ascending_order_with_limited_locators(self):
        driver = self.driver
        driver.get('http://the-internet.herokuapp.com/tables')
        driver.find_element_by_css_selector('#table1 thead tr th:nth-of-type(3)').click()
        email_column = driver.find_elements_by_css_selector('#table1 tbody tr td:nth-of-type(3)')
        emails = [email.text for email in email_column]
        assert emails == sorted(emails)

if __name__ == "__main__":
    unittest.main()

Ruby

# filename: table_sort.rb

require 'selenium-webdriver'
require 'rspec/expectations'
include RSpec::Matchers

def setup
  @driver = Selenium::WebDriver.for :firefox
end

def teardown
  @driver.quit
end

def run
  setup
  yield
  teardown
end

run do
  @driver.get 'http://the-internet.herokuapp.com/tables'

  @driver.find_element(css: '#table1 thead tr th:nth-of-type(4)').click

  dues = @driver.find_elements(css: '#table1 tbody tr td:nth-of-type(4)')
  due_values = dues.map { |due| due.text.delete('$').to_f }

  expect(due_values).to eql due_values.sort
end

run do
  @driver.get 'http://the-internet.herokuapp.com/tables'

  @driver.find_element(css: '#table1 thead tr th:nth-of-type(4)').click
  @driver.find_element(css: '#table1 thead tr th:nth-of-type(4)').click

  dues = @driver.find_elements(css: '#table1 tbody tr td:nth-of-type(4)')
  due_values = dues.map { |due| due.text.delete('$').to_f }

  expect(due_values).to eql due_values.sort.reverse
end

run do
  @driver.get 'http://the-internet.herokuapp.com/tables'

  @driver.find_element(css: '#table1 thead tr th:nth-of-type(3)').click

  emails = @driver.find_elements(css: '#table1 tbody tr td:nth-of-type(3)')
  email_values = emails.map { |email| email.text }

  expect(email_values).to eql email_values.sort
end


C#

// filename: Tables.cs
using NUnit.Framework;
using OpenQA.Selenium;
using OpenQA.Selenium.Firefox;
using System;
using System.Collections.Generic;

public class Tables
{
    IWebDriver Driver;

    [SetUp]
    public void SetUp()
    {
        Driver = new FirefoxDriver();
    }

    [TearDown]
    public void TearDown()
    {
        Driver.Quit();
    }

    [Test]
    public void TableWithNoHelpfulMarkup1()
    {
        Driver.Navigate().GoToUrl("http://the-internet.herokuapp.com/tables");
        Driver.FindElement(By.CssSelector("#table1 thead tr th:nth-of-type(4)")).Click();

        IReadOnlyCollection<IWebElement> Dues = Driver.FindElements(By.CssSelector("#table1 tbody tr td:nth-of-type(4)"));
        List<double> FormattedDues = new List<double>();
        foreach(IWebElement Due in Dues)
        {
            FormattedDues.Add(double.Parse(Due.Text.Replace("$", "")));
        }

        Assert.That(FormattedDues, Is.Ordered);
    }

    [Test]
    public void TableWithNoHelpfulMarkup2()
    {
        Driver.Navigate().GoToUrl("http://the-internet.herokuapp.com/tables");
        Driver.FindElement(By.CssSelector("#table1 thead tr th:nth-of-type(4)")).Click();
        Driver.FindElement(By.CssSelector("#table1 thead tr th:nth-of-type(4)")).Click();

        IReadOnlyCollection<IWebElement> Dues = Driver.FindElements(By.CssSelector("#table1 tbody tr td:nth-of-type(4)"));
        List<double> FormattedDues = new List<double>();
        foreach(IWebElement Due in Dues)
        {
            FormattedDues.Add(double.Parse(Due.Text.Replace("$", "")));
        }

        Assert.That(FormattedDues, Is.Ordered.Descending);
    }

    [Test]
    public void TableWithNoHelpfulMarkup3()
    {
        Driver.Navigate().GoToUrl("http://the-internet.herokuapp.com/tables");
        Driver.FindElement(By.CssSelector("#table1 thead tr th:nth-of-type(3)")).Click();

        IReadOnlyCollection<IWebElement> Emails = Driver.FindElements(By.CssSelector("#table1 tbody tr td:nth-of-type(3)"));
        List<string> FormattedEmails = new List<string>();
        foreach(IWebElement Email in Emails)
        {
            FormattedEmails.Add(Email.Text);
        }

        Assert.That(FormattedEmails, Is.Ordered);
    }
}

If you save this file and run it here is what will happen:

  • Open the browser
  • Load the page
  • Click the column heading
  • Grab the values for that column
  • Assert that the columns are sorted in the correct order (ascending or descending)
  • Close the browser

Leave a Reply

Your email address will not be published.