Why is unit testing essential for test automation?



Using unit tests in a test automation project brings a lot of benefits

  1. create short test automation scripts
  2. have independent test automation scripts
  3. have easy to maintain code


What follows is a very long article about why unit testing is essential for test automation.

It includes real code that you can use by yourself.

If you have any questions on the code or the article, please leave them in the Comments section.

Enjoy!



TEST AUTOMATION WITHOUT UNIT TESTS



Lets start with a sample project that aims at automating 2 test cases.

Both test cases describe user scenarios for the Vancouver Public Library site:


Test Case 1

1. open the home page of the site (http://www.vpl.ca)

2. Check that the home page title is correct

3. Do a keyword search; the results page opens after the keyword search



4. Check that the results page title is correct

5. Click on the first result of the results page; the details page opens



6. On the details page, check that the book title is displayed and not empty

7. On the details page, check that the book author is displayed and not empty




Test Case 2

1. open the home page of the site (http://www.vpl.ca)

2. Check that the home page title is correct

3. Do a keyword search; the results page opens after the keyword search

4. Check that the results page title is correct

5. Check that the results count is displayed and is correct for page 1

6. click on page 2 of results



7. Check that the results count is displayed and is correct for page 2



See below the code of the project.

It uses Java as the programming language.



//TEST CASE 1: book title and author are displayed and not empty



import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.firefox.FirefoxDriver;

public class LibraryTestCases {

public static void main(String[] args) {

WebDriver driver = new FirefoxDriver();

//open the home page and check that the home page title is correct
driver.get("http://www.vpl.ca");

if (driver.getTitle().equalsIgnoreCase("Vancouver Public Library - Home") == true)
 System.out.println("correct home page title");
else
{
 System.out.println("incorrect home page title, stop application");
 System.exit(0);
}

//search with a keyword, the results page is opened as result of the search
WebElement searchTextBox;
searchTextBox = driver.findElement(By.xpath("//input[@id='globalQuery']"));

searchTextBox.click();
searchTextBox.sendKeys("java");

WebElement searchButton;
searchButton = driver.findElement(By.xpath("//input[@class='search_button']"));
searchButton.click();

//check that the results page title is correct  
if (driver.getTitle().equalsIgnoreCase("Search | Vancouver Public Library | BiblioCommons") == true)
 System.out.println("correct results page title");
else
{
 System.out.println("incorrect results page title, stop application");
 System.exit(0);
}

//click on the first result, the details page is opened as a result
WebElement firstResult;
firstResult = driver.findElement(By.xpath("(//a[@testid='bib_link'])[1]"));  
firstResult.click();

//check that the title of details page is correct
if (driver.getTitle().indexOf("Vancouver Public Library | BiblioCommons") >= 0)
 System.out.println("correct details page title");
else
{
 System.out.println("incorrect details page title, stop the application");
 System.exit(0);
}
    
//check that the book title is displayed and not empty
WebElement bookTitle = driver.findElement(By.xpath("//h1[@id='item_bib_title']"));

if (bookTitle.isDisplayed() == true)
 System.out.println("book title displayed");
else
{
 System.out.println("book title not displayed, stop application");
 System.exit(0);
}

if (bookTitle.getText().length() > 0)
 System.out.println("the book title not empty");
else
{
 System.out.println("book title empty, stop application");
 System.exit(0);
}


//check that the book author is displayed and not empty
WebElement bookAuthor;
bookAuthor = driver.findElement(By.xpath("//a[@testid='author_search']"));

if (bookAuthor.isDisplayed() == true)
 System.out.println("book author displayed");
else
{
 System.out.println("book author not displayed, stop application");
 System.exit(0);
}

if (bookAuthor.getText().length() > 0)
 System.out.println("book author not empty");
else
{
 System.out.println("book author empty, stop application");
 System.exit(0);
}



//TEST CASE 2: browsing through results pages works


//go back to results page
driver.navigate().back();


//check that the number of results is displayed and correct
WebElement numberResultPageOne;
numberResultPageOne = driver.findElement(By.xpath("//span[@class='items_showing_count']"));

if (numberResultPageOne.isDisplayed() == true)
 System.out.println("Results number on page 1 - displayed");
else
{
 System.out.println("Results number on page 1 - not displayed, stop app");
 System.exit(0);
}

if (numberResultPageOne.getText().indexOf("1 - 25") >=0)
 System.out.println("Results number on page 1 - correct");
else
{
 System.out.println("Results number on page 1 - not correct, stop app");
 System.exit(0);
}


//navigating to page 2 of results
WebElement pageTwo = driver.findElement(By.xpath("//a[@testid='link_page2']"));
pageTwo.click();


//check that the number of results is displayed and correct
WebElement numberResultPageTwo;
numberResultPageTwo  = driver.findElement(By.xpath("//span[@class='items_showing_count']"));

if (numberResultPageTwo.isDisplayed() == true)
 System.out.println("Results number on page 2 - displayed");
else
{
 System.out.println("Results number on page 2 - not displayed, stop app");
 System.exit(0);
}

if (numberResultPageTwo.getText().indexOf("26 - 50") >=0)
 System.out.println("Results number on page 2 - correct");
else
{
 System.out.println("Results number on page 2 - not correct, stop app");
 System.exit(0);
}

//close the browser
driver.quit();


}

}


The code goes through the following phases:

1. create the browser driver object; the Firefox browser is loaded as a result

2. Test Case 1 code; it goes from home page to results page and from the results page to details page

3. go back from details page to results page; this is needed because test case 2 happens on results page

going back from details page to results page is done to avoid going through the home page for test case 2

4. Test Case 2 code

5. quit the browser driver object; the Firefox browser is closed as a result


Each test case has a few verifications.

Each verification logs information to the console panel.

If the verification passes, the code execution continues.

If the verification fails, the application is stopped.



WHAT IS WRONG WITH THIS SAMPLE PROJECT?


The sample code is not efficient for multiple reasons:

1. if one of the test case 1 verifications fails, the application stops; test case 2 is completely ignored

2. to understand if the test cases passed or failed, the test automation engineer needs to read all information from the console panel

3. the code is long; its maintenance will not be easy

How do we fix these issues?




UNIT TESTING TO THE RESCUE

First, some unit testing basics.

We will continue with a code example shortly.
  • Unit testing is being done with the help of unit testing frameworks.
  • With a unit testing framework, developers create short unit test scripts that test their application code.
  • The unit tests are code that tests the application code.
  • Each unit test has the purpose of testing a single application entity (one class, one object).
  • Each of the unit tests is independent of the other unit tests.
  • Unit tests are being created in test classes.
  • They can be identified from normal class methods through annotations.
  • A unit test verifies if the tested entity works as expected. This is done using assertions.
  • Developer can create multiple unit tests for his application code. Test runners are used for running both individual and multiple unit tests.
  • Before executing unit tests, the environment may need to be set up. The environment should be cleaned up after the unit test executes. 
  • The set up and cleaning of the environment is being done through test fixtures.




TEST AUTOMATION WITH UNIT TESTS


I will refactor the sample project in a few different steps:
  1. break the code in unit tests
  2. use assertions for test case verifications
  3. use test fixtures for setting up and cleaning the test environment
The JUNIT framework will be used in the code examples.



The code can be changed easily to use the Test NG framework.



1. BREAK THE CODE IN UNIT TEST SCRIPT


Lets see how the code can be broken down.

I will use a test class that does not have a main method and includes 2 unit test scripts (one for each test case).

Each unit test is prefixed with the @Test annotation.




import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.firefox.FirefoxDriver;

public class LibraryTestCases {


//Test Case 1: book title and author are displayed and not empty


@Test
public void testResultDetails()
{

WebDriver driver = new FirefoxDriver();

//open the home page and check that the home page title is correct
driver.get("http://www.vpl.ca");

if (driver.getTitle().equalsIgnoreCase("Vancouver Public Library - Home") == true)
 System.out.println("the home page title is correct");
else
{
 System.out.println("the home page title is incorrect, stop the application");
 System.exit(0);
}

//search with a keyword, the results page is opened as result of the search
WebElement searchTextBox = driver.findElement(By.xpath("//input[@id='globalQuery']"));
searchTextBox.click();
searchTextBox.sendKeys("java");

WebElement searchButton = driver.findElement(By.xpath("//input[@class='search_button']"));
searchButton.click();

//check that the results page title is correct  
if (driver.getTitle().equalsIgnoreCase("Search | Vancouver Public Library | BiblioCommons") == true)
 System.out.println("the results page title is correct");
else
{
 System.out.println("the results page title is incorrect, stop the application");
 System.exit(0);
}

//click on the first result, the details page is opened as a result
WebElement firstResult = driver.findElement(By.xpath("(//a[@testid='bib_link'])[1]"));  
firstResult.click();

//check that the title of details page is correct
if (driver.getTitle().indexOf("Vancouver Public Library | BiblioCommons") >= 0)
 System.out.println("the details page title is correct");
else
{
 System.out.println("the details page title is incorrect, stop the application");
 System.exit(0);
}

//check that the book title is displayed and not empty
WebElement bookTitle = driver.findElement(By.xpath("//h1[@id='item_bib_title']"));

if (bookTitle.isDisplayed() == true)
 System.out.println("the book title is displayed");
else
{
 System.out.println("the book title is not displayed, stop the application");
 System.exit(0);
}

if (bookTitle.getText().length() > 0)
 System.out.println("the book title is not empty");
else
{
 System.out.println("the book title is empty, stop the application");
 System.exit(0);
}

//check that the book author is displayed and not empty
WebElement bookAuthor = driver.findElement(By.xpath("//a[@testid='author_search']"));

if (bookAuthor.isDisplayed() == true)
 System.out.println("the book author is displayed");
else
{
 System.out.println("the book author is not displayed, stop the application");
 System.exit(0);
}

if (bookAuthor.getText().length() > 0)
 System.out.println("the book author is not empty");
else
{
 System.out.println("the book author is empty, stop the application");
 System.exit(0);
}

//close the browser
driver.quit();

} 

//Test Case 2: browsing through results page works



@Test 
public void testPaging()
{

WebDriver driver = new FirefoxDriver();

//open the home page and check that the home page title is correct
driver.get("http://www.vpl.ca");

if (driver.getTitle().equalsIgnoreCase("Vancouver Public Library - Home") == true)
 System.out.println("the home page title is correct");
else
{
 System.out.println("the home page title is incorrect, stop the application");
 System.exit(0);
}

//search with a keyword, the results page is opened as result of the search
WebElement searchTextBox = driver.findElement(By.xpath("//input[@id='globalQuery']"));
searchTextBox.click();
searchTextBox.sendKeys("java");

WebElement searchButton = driver.findElement(By.xpath("//input[@class='search_button']"));
searchButton.click();

//check that the results page title is correct  
if (driver.getTitle().equalsIgnoreCase("Search | Vancouver Public Library | BiblioCommons") == true)
 System.out.println("the results page title is correct");
else
{
 System.out.println("the results page title is incorrect, stop the application");
 System.exit(0);
}

//check that the number of results is displayed and correct
WebElement numberResultPageOne = driver.findElement(By.xpath("//span[@class='items_showing_count']"));

if (numberResultPageOne.isDisplayed() == true)
 System.out.println("The number of the results on the page 1 is displayed");
else
{
 System.out.println("The number of the results on the page 1 is not displayed, stop the application");
 System.exit(0);
}

if (numberResultPageOne.getText().indexOf("1 - 25") >=0)
 System.out.println("The number of the results on the page 1 is correct");
else
{
 System.out.println("The number of the results on the page 1 is not correct, stop the application");
 System.exit(0);
}

//navigating to page 2 of results
WebElement pageTwo = driver.findElement(By.xpath("//a[@testid='link_page2']"));
pageTwo.click();

//check that the number of results is displayed and correct
WebElement numberResultPageTwo = driver.findElement(By.xpath("//span[@class='items_showing_count']"));

if (numberResultPageTwo.isDisplayed() == true)
 System.out.println("The number of the results on the page 2 is displayed");
else
{
 System.out.println("The number of the results on the page 2 is not displayed, stop the application");
 System.exit(0);
}

if (numberResultPageTwo.getText().indexOf("26 - 50") >=0)
 System.out.println("The number of the results on the page 2 is correct");
else
{
 System.out.println("The number of the results on the page 2 is not correct, stop the application");
 System.exit(0);
}

//close the browser
driver.quit();

}

}

The changes done so far are that

  • the test class does not have a main method any longer
  • the code of each test case is included in a separate unit test; each unit test uses the @Test annotations
  • executing the test class will execute both unit tests; if one of the unit tests fails, the second unit test continues
  • both unit tests have a similar structure:
    • create the Selenium WebDriver browser driver object
    • open the site
    • implement the test case
    • quit the browser driver object

It is because of this structure that both unit tests are independent.

The result of executing the unit tests is a green bar.

The unit tests are still logging information to the console panel about their verifications.

Executing the scripts shows a green execution bar meaning that all unit tests passed:






The green bar is displayed even if one of the unit tests has an error.

We need to use assertions so that the bar is red in case of an error.




2. USE ASSERTIONS FOR TEST CASE VERIFICATIONS

All verifications will be replaced next with assertions.

Assertions get a condition that needs to be verified.

The condition is evaluated.

If the condition's result is true, the assertion passes and the unit test continues.

If the condition fails, the assertion fails and the unit test fails as well.



Assertions do not log any information to the console.

An error message can be logged to the Failure panel.

The error message is optional.

import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.firefox.FirefoxDriver;

public class LibraryTestCases {

//Test Case 1: book title and author are displayed and not empty

@Test
public void testResultDetails()
{

WebDriver driver = new FirefoxDriver();

//open the home page and check that the home page title is correct
driver.get("http://www.vpl.ca");

assertTrue("incorrect home page title", 
driver.getTitle().equalsIgnoreCase("Vancouver Public Library - Home") == true);

//search with a keyword, the results page is opened as result of the search
WebElement searchTextBox = driver.findElement(By.xpath("//input[@id='globalQuery']"));
searchTextBox.click();
searchTextBox.sendKeys("java");

WebElement searchButton = driver.findElement(By.xpath("//input[@class='search_button']"));
searchButton.click();

//check that the results page title is correct  
assertTrue("results page title incorrect", 
driver.getTitle().equalsIgnoreCase("Search | Vancouver Public Library | BiblioCommons") == true);

//click on the first result, the details page is opened as a result
WebElement firstResult = driver.findElement(By.xpath("(//a[@testid='bib_link'])[1]")); 
firstResult.click();

//check that the title of details page is correct
assertTrue ("the details page title is incorrect", 
driver.getTitle().indexOf("Vancouver Public Library | BiblioCommons") >= 0);

//check that the book title is displayed and not empty
WebElement bookTitle = driver.findElement(By.xpath("//h1[@id='item_bib_title']"));

assertTrue ("the book title is not displayed", bookTitle.isDisplayed() == true);

assertTrue ("the book title is empty", bookTitle.getText().length() > 0);

//check that the book author is displayed and not empty
WebElement bookAuthor = driver.findElement(By.xpath("//a[@testid='author_search']"));

assertTrue ("the book author is not displayed", bookAuthor.isDisplayed() == true)

assertTrue ("the book author is empty", bookAuthor.getText().length() > 0);

//close the browser
driver.quit();

} 

//Test Case 2: browsing through results pages works

@Test 
public void testPaging()
{

WebDriver driver = new FirefoxDriver();

//open the home page and check that the home page title is correct
driver.get("http://www.vpl.ca");

assertTrue ("the home page title is incorrect", 
driver.getTitle().equalsIgnoreCase("Vancouver Public Library - Home") == true);

//search with a keyword, the results page is opened as result of the search
WebElement searchTextBox = driver.findElement(By.xpath("//input[@id='globalQuery']"));
searchTextBox.click();
searchTextBox.sendKeys("java");

WebElement searchButton = driver.findElement(By.xpath("//input[@class='search_button']"));
searchButton.click();

//check that the results page title is correct  
assertTrue ("results page title incorrect",
driver.getTitle().equalsIgnoreCase("Search | Vancouver Public Library | BiblioCommons") == true);

//check that the number of results is displayed and correct
WebElement numberResultPageOne = driver.findElement(By.xpath("//span[@class='items_showing_count']"));

assertTrue ("page 1 results number is not displayed",
numberResultPageOne.isDisplayed() == true);

assertTrue ("page 1 results number is incorrect",
pagenumberResultPageOne.getText().indexOf("1 - 25") >=0);

//navigating to page 2 of results
WebElement pageTwo = driver.findElement(By.xpath("//a[@testid='link_page2']"));
pageTwo.click();

//check that the number of results is displayed and correct
WebElement numberResultPageTwo = driver.findElement(By.xpath("//span[@class='items_showing_count']"));

assertTrue ("page 2 results number is not displayed",
numberResultPageTwo.isDisplayed() == true);

assertTrue ("page 2 results number is not correct",
numberResultPageTwo.getText().indexOf("26 - 50") >=0);

//close the browser
driver.quit();

}

}


Using assertions for test case verifications helps in the following ways:

  • assertions do not log information to the console log
  • the unit test continues to execute only if the assertion passes
  • if the assertion fails, the unit test fails
  • using assertions, the only thing that needs to be checked after executing the unit tests is if the progress bar is red or green

If the unit tests pass again, the green bar is displayed.

If one of the assertions fails, the unit test fails and we get a red bar:




The assertion error message is displayed in the Failure Trace:




3. USE TEST FIXTURES FOR PREPARING AND CLEANING THE TEST ENVIRONMENT


Each of the unit tests depends on the test environment to be set up before the unit test runs.

By setting up the test environment, I understand creating the browser driver object:

driver = new FirefoxDriver();

After the unit test finishes execution, the test environment is cleaned up by closing the browser driver object:

driver.quit();

Currently, the browser driver object is created and closed in each of the test scripts.

We can use JUNIT fixtures for moving this code outside of the test scripts.



See below the refactored code:

import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.firefox.FirefoxDriver;

public class LibraryTestCases {

WebDriver driver;

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

@After
public void tearDown()
{
driver.quit();
}


//Test Case 1: book title and author are displayed and not empty


@Test
public void testResultDetails()
{

//open the home page and check that the home page title is correct
driver.get("http://www.vpl.ca");

assertTrue("the home page title is incorrect", 
driver.getTitle().equalsIgnoreCase("Vancouver Public Library - Home") == true);

//search with a keyword, the results page is opened as result of the search
WebElement searchTextBox = driver.findElement(By.xpath("//input[@id='globalQuery']"));
searchTextBox.click();
searchTextBox.sendKeys("java");

WebElement searchButton = driver.findElement(By.xpath("//input[@class='search_button']"));
searchButton.click();

//check that the results page title is correct  
assertTrue("the results page title is incorrect", 
driver.getTitle().equalsIgnoreCase("Search | Vancouver Public Library | BiblioCommons") == true);

//click on the first result, the details page is opened as a result
WebElement firstResult = driver.findElement(By.xpath("(//a[@testid='bib_link'])[1]")); 
firstResult.click();

//check that the title of details page is correct
assertTrue ("the details page title is incorrect", 
driver.getTitle().indexOf("Vancouver Public Library | BiblioCommons") >= 0);

//check that the book title is displayed and not empty
WebElement bookTitle = driver.findElement(By.xpath("//h1[@id='item_bib_title']"));

assertTrue ("the book title is not displayed", bookTitle.isDisplayed() == true);
assertTrue ("the book title is empty", bookTitle.getText().length() > 0);

//check that the book author is displayed and not empty
WebElement bookAuthor = driver.findElement(By.xpath("//a[@testid='author_search']"));

assertTrue ("the book author is not displayed", bookAuthor.isDisplayed() == true)
assertTrue ("the book author is empty", bookAuthor.getText().length() > 0);

} 


//Test Case 2: browsing through results pages works

@Test 
public void testPaging()
{   

//open the home page and check that the home page title is correct
driver.get("http://www.vpl.ca");

assertTrue ("the home page title is incorrect", 
driver.getTitle().equalsIgnoreCase("Vancouver Public Library - Home") == true);

//search with a keyword, the results page is opened as result of the search
WebElement searchTextBox = driver.findElement(By.xpath("//input[@id='globalQuery']"));
searchTextBox.click();
searchTextBox.sendKeys("java");

WebElement searchButton = driver.findElement(By.xpath("//input[@class='search_button']"));
searchButton.click();

//check that the results page title is correct  
assertTrue ("the results page title is incorrect",
driver.getTitle().equalsIgnoreCase("Search | Vancouver Public Library | BiblioCommons") == true);

//check that the number of results is displayed and correct
WebElement numberResultPageOne = driver.findElement(By.xpath("//span[@class='items_showing_count']"));

assertTrue ("page 1 results number is not displayed",
numberResultPageOne.isDisplayed() == true);

assertTrue ("page 1 results number is incorrect",
pagenumberResultPageOne.getText().indexOf("1 - 25") >=0);

//navigating to page 2 of results
WebElement pageTwo = driver.findElement(By.xpath("//a[@testid='link_page2']"));
pageTwo.click();

//check that the number of results is displayed and correct
WebElement numberResultPageTwo = driver.findElement(By.xpath("//span[@class='items_showing_count']"));

assertTrue ("page 2 results number is not displayed",
numberResultPageTwo.isDisplayed() == true);

assertTrue ("page 2 results number is not correct",
numberResultPageTwo.getText().indexOf("26 - 50") >=0);

}

}
A few changes have been done:
  • the driver object is declared outside of the test scripts
  • for the test environment set up, the setUp() method is created using the @Before annotation;
The browser driver object is created in this method.

Because of the @Before annotation, the setUp() method runs automatically before each unit test
  • for the test environment clean up, the tearDown() method is created using the @After annotation;
The browser driver object is closed in this method.

Because of the @After annotation, the tearDown() method runs automatically after each unit test.

Using test fixtures, the execution order becomes:

  1. setUp()
  2. unit test()
  3. tearDown()
  4. setUp()
  5. unit test()
  6. tearDown()



If you liked this article, please share it with your friends.



Create dependent automation scripts with Test NG



There are situations when you need your test automation scripts to run in a specific order.

For example, if you are automating user scenarios for an ecommerce site, you may have test scripts such as

searchForProduct() - tests if there are valid products on the site

addProductToCart() - tests if products can be added to the cart

checkoutAndPay() - test that the checkout process works correctly

It is possible that sometimes the site does not have valid products available.

Without valid products, nothing can be added to the cart.

So if the searchForProduct() test script fails because there are no valid products available, there is no point executing the other 2 test scripts.

In this case, searchForProduct() fails and addProductToCart() and checkoutAndPay() should be skipped.


How can you have dependent test scripts in JUNIT?



JUNIT does not have good support for running test scripts in a specific order.

You can use @FixMethodOrder(MethodSorters.NAME_ASCENDING) for running the test scripts in the ascending name order.

And this is about everything that you can do.



How can you have dependent test scripts in TestNG?


Going back to the example already discussed, we have a test class with 3 test scripts:

package tests;

import org.testng.annotations.Test;

public class RandomCheckOutTests {

@Test
public void checkOutAndPay()
{
  System.out.println("3. checkOutAndPay Completed ");
}

@Test
public void addProductToCart()
{
  System.out.println("2. addProductToCart Completed");
}

@Test
public void searchForProduct()
{

  System.out.println("1. searchForProduct Completed");

}
 
}

Running the test class generates the following result:

[TestNG] Running:
C:\Users\asiminiuc\AppData\Local\Temp\testng-eclipse--2137197999\testng-customsuite.xml


2. addProductToCart Completed
3. checkOutAndPay Completed
1. searchForProduct Completed
PASSED: addProductToCart
PASSED: checkOutAndPay
PASSED: searchForProduct


===============================================
Default test
Tests run: 3, Failures: 0, Skips: 0
===============================================


By default, the test scripts are executed in a random order.
addProductToCart() runs before searchForProduct().

Lets see what happens if the searchForProduct() script fails:

package tests;

import static org.testng.AssertJUnit.assertTrue;
import org.testng.annotations.Test;

public class RandomCheckOutTests {

@Test
public void checkOutAndPay()
{
  System.out.println("3. checkOutAndPay Completed ");
}
 
@Test
public void addProductToCart()
{
  System.out.println("2. addProductToCart Completed");
}

@Test
public void searchForProduct()
{

  System.out.println("1. searchForProduct Completed");

  assertTrue(false);

}
 
}


The results of executing the test scripts are:

[TestNG] Running:
C:\Users\asiminiuc\AppData\Local\Temp\testng-eclipse--1193208089\testng-customsuite.xml


2. addProductToCart Completed
3. checkOutAndPay Completed
1. searchForProduct Completed
PASSED: addProductToCart
PASSED: checkOutAndPay
FAILED: searchForProduct

===============================================
Default test
Tests run: 3, Failures: 1, Skips: 0
===============================================


searchForProduct() fails and addProductToCart() and checkoutAndPay() are executed as well.

This does not make sense.

If searchForProduct() fails, addProductToCart() and checkOutAndPay() should be ignored.

We need to make addProductToCart() and checkOutAndPay() dependent on the sucess or failure of searchForProduct().

To make the scripts dependent, we need to use the dependsOnMethods or dependsOnGroups annotations:

package tests;

import org.testng.annotations.Test;

public class OrderedCheckOutTests {

@Test (dependsOnMethods = "addProductToCart")
public void checkOutAndPay()
{
  System.out.println("3. checkOutAndPay Completed ");  
}
 
@Test (dependsOnMethods = "searchForProduct")
public void addProductToCart()
{
 System.out.println("2. addProductToCart Completed");
}

@Test
public void searchForProduct()
{
 System.out.println("1. searchForProduct Completed");
}
}

In this case, addProductOnCart() depends on searchForProduct().
searchForProduct() will run before addProductOnCart().

checkOutAndPay() depends on addProductToCart().
addProductToCart() will run before checkOutAndPay().


This is the result of running the test scripts:

[TestNG] Running:
C:\Users\asiminiuc\AppData\Local\Temp\testng-eclipse-1181658048\testng-customsuite.xml

1. searchForProduct Completed
2. addProductToCart Completed
3. checkOutAndPay Completed
PASSED: searchForProduct
PASSED: addProductToCart
PASSED: checkOutAndPay

===============================================
Default test
Tests run: 3, Failures: 0, Skips: 0
===============================================


The scripts will always run in the following order:

searchForProduct
addProductToCart
checkOutAndPay



What if one of the test scripts fails?


I will make the searchForProduct() script fail with an assertion:

package tests;

import static org.testng.AssertJUnit.assertTrue;
import org.testng.annotations.Test;

public class OrderedCheckOutTests {

@Test (dependsOnMethods = "addProductToCart")
public void checkOutAndPay()
{
  System.out.println("3. checkOutAndPay Completed ");  
}

@Test (dependsOnMethods = "searchForProduct")
public void addProductToCart()
{
  System.out.println("2. addProductToCart Completed");
}
 
@Test
public void searchForProduct()
{
  System.out.println("1. searchForProduct Completed");

  assertTrue(false);
}

}


When the test scripts are executed again, searchForProduct fails and the other 2 test scripts are skipped:

[TestNG] Running:
C:\Users\asiminiuc\AppData\Local\Temp\testng-eclipse-349769629\testng-customsuite.xml

1. searchForProduct Completed
FAILED: searchForProduct
java.lang.AssertionError:

SKIPPED: addProductToCart
java.lang.Throwable: Method OrderedCheckOutTests.addProductToCart()[pri:0, instance:tests.OrderedCheckOutTests@71e7a66b] depends on not successfully finished methods


SKIPPED: checkOutAndPay
java.lang.Throwable: Method OrderedCheckOutTests.checkOutAndPay()[pri:0, instance:tests.OrderedCheckOutTests@71e7a66b] depends on not successfully finished methods

===============================================
Default test
Tests run: 3, Failures: 1, Skips: 2
===============================================




Is this the only way for having dependent test scripts?

DependsOnMethods is just one of the ways of making test scripts dependent.

The other method is DependOnGroups:

package tests;

import org.testng.annotations.Test;

public class OrderedCheckOutTests2 {

@Test (dependsOnGroups = "search")
public void checkOutAndPay()
{
  System.out.println("3. checkOutAndPay Completed ");  
}

@Test (groups = "search")
public void addProductToCart()
{
  System.out.println("2. addProductToCart Completed");
}

@Test (groups = "search")
public void searchForProduct()
{
  System.out.println("1. searchForProduct Completed");  
}

}


Before the checkOutAndPay script runs, the scripts from the search groups are executed.

The scripts from the group are executed in a random order.


READ NEXT: 
Why is unit testing essential for test automation?

WebDriver test automation is like driving a taxi

What is Selenium WebDriver?

The official site says that
WebDriver is a tool for automating web application testing, and in particular to verify that they work as expected.

It aims to provide a friendly API that’s easy to explore and understand, easier to use than the Selenium-RC (1.0) API, which will help to make your tests easier to read and maintain.

It’s not tied to any particular test framework, so it can be used equally well in a unit testing or from a plain old “main” method.
Testers with only manual testing experience might have questions about this definition.

What is a framework?

What is an API?

How does Selenium-RC works?

What is unit testing?

Too many questions instead of an answer.




Is there an easier way of understanding what WebDriver test automation is? 

Can test automation with Selenium WebDriver be explained in very simple terms so that everyone can understand it, even without having any technical knowledge?

An easy way of explaining how a new thing works is through an analogy with something else that you already know.




The WebDriver name points to the analogy.

Everyone knows what a driver is and what it does.

Many people drive a car.

Those who dont drive a car use the services of taxi drivers.



Who participates in taxi driving?

3 actors are involved: 

  • the customer
  • the taxi driver
  • the car
What happens while driving a taxi?

Dialogues between the 3 actors involved.



So this is the analogy to use to explain test automation with WebDriver.

Test automation with WebDriver is like driving a taxi.

It happens through dialogues between the 3 actors involved.



Lets have a closer look at taxi driving.

A customer gets into a cab.

He gives instructions to the taxi driver about 
  • the address and 
  • the route to take

The cab driver drives the taxi, the car in this case.

While driving the taxi, the driver gives commands to the car using the wheel, pedals, etc.



The driver receives information about the cab’s status on the meters and makes decisions based on it.

The car executes the taxi driver commands to get to the desired address.




How does this help with understanding test automation?

Let's ask about test automation the same questions asked for driving a taxi.


Who participates in test automation?

There are 3 actors in test automation with WebDriver:
  • tester that writes the automation code
  • browser driver
  • browser
What happens during test automation?

Dialogue between the 3 actors involved.
How do the analogy between taxi driving and web driver test automation work?

Let's see:

A test engineer is like a taxi customer.

The test automation code is like the customer instructions to the taxi driver.

The browser driver object is like the taxi driver.

The browser is like a car.




Does it start making sense?

Taxi driving versus test automation?



Let's have a closer look at test automation.

The test engineer writes code with instructions for the browser driver object.

He tells the browser driver object to 
  • open the site and 
  • interact with the site elements.

The browser driver drives the browser.

It does this by sending commands to the browser.
The browser driver receives information from the browser about 
  • status of site elements (are they visible, are they enabled)
  • values of site elements 
and makes decisions based on it.

The browser executes the commands received from the browser driver.



Is the analogy clear now?