JUnit Tutorials

HOME

JUnit is an open source Unit Testing Framework for JAVA.
JUnit is a simple framework to write repeatable tests. It is an instance of the xUnit architecture for unit testing frameworks.

JUnit4

Chapter 1 How to configure Junit in Intellij
Chapter 2 How to run JUnit5 tests through Command Line
Chapter 3 JUnit4 Assertions
Chapter 4 How to Parameterize tests in JUnit4
Chapter 5 Integration of Cucumber with Selenium and JUnit
Chapter 6 Integration of Serenity with Cucumber6 and JUnit5
Chapter 7 Integration of Serenity with JUnit4
Chapter 8 Rest API Test in Cucumber BDD
Chapter 9 Difference between JUnit4 and JUnit5

JUnit5

Chapter 1 JUnit5 Assertions Example
Chapter 2 Grouped Assertions in JUnit 5 – assertAll()
Chapter 3 How to Retry Test in JUnit5 – @RepeatedTest
Chapter 4 How to disable tests in JUnit5 – @Disabled
Chapter 5 How to run JUnit5 tests in order
Chapter 6 How to tag and filter JUnit5 tests – @Tag
Chapter 7 Assumptions in JUnit5
Chapter 8 How to parameterized Tests in JUnit5
Chapter 9 How to run parameterized Selenium test using JUnit5
Chapter 10 Integration of Serenity with JUnit5
Chapter 11 Integration of Serenity with Cucumber6 and JUnit5

Gradle

Chapter 1 Gradle – Allure Report for Selenium and JUnit4
Chapter 2 Gradle Project with Cucumber, Selenium and JUnit4
Chapter 3 Gradle – Integration of Selenium and JUnit5

How to run Serenity BDD tests in Chrome Browser

HOME

Serenity BDD has strong WebDriver integration, and manages the WebDriver instances. It is not needed to create or close the WebDriver instance of the Serenity Tests.

Serenity uses a library WebDriver Manager which manages the driver for us. We don’t need to explicitly download and configure the WebDriver binaries for us.

The default browser of Serenity is Firefox.

    @Managed(driver = "chrome")
    WebDriver driver;

@Managed annotation in Serenity will manage the WebDriver instance, including opening the appropriate driver at the start of each test, and shutting it down when the test is finished. @Managed provides an option to the user to select the WebDriver driver to the run the tests in it. The possible values are firefoxchromeiexplorerphantomjsappiumsafariedge and htmlunit . There are multiple ways to manage the WebDriver. One of the way is shown below:

In the below program, the tests are running on Chrome browser. The driver name is mentioned with @Managed annotation.

@RunWith(SerenityRunner.class)
public class ChromeTest {

    @Managed(driver = "chrome")
    WebDriver driver;

    @Steps
    NavigateActions navigate;

    @Test
    public void openBrowser()
    {
        navigate.toTheHomePage();
    }

}

NavigateActions

public class NavigateActions extends UIInteractionSteps {

    @Step
    public void toTheHomePage() {
         openUrl("https://opensource-demo.orangehrmlive.com/");
    }
}

There is another way to assign Chrome to the WebDriver. This can be defined in serenity.config or serenity.properties.

serenity.config

webdriver{
    driver = chrome

}

serenity.properties

webdriver.driver = chrome

When the webdriver is defined in properties file, it is not needed to redefine it in the Test as shown below:

@RunWith(SerenityRunner.class)
public class ChromeTest {

    @Managed
    WebDriver driver;

    @Steps
    NavigateActions navigate;

    @Test
    public void openBrowser()
    {
        navigate.toTheHomePage();
    }

}

Manually Configure ChromeDriver

To run your web tests with a given driver, download the correct driver binary and place it under src/test resources. The chromedriver binary can be downloaded from here – ChromeDriver – WebDriver for Chrome (chromium.org)

It is always advisable to create a drivers directory under src/test/resources. The tests can be run on different Operating System, so create three sub-directories named as windows, mac, linux within drivers directory. Place the driver binary in these directories as shown below :

Below is the sample Serenity.config for the chrome driver.

webdriver{
    driver = chrome

}

drivers {
  windows {
     webdriver.chrome.driver = "src/test/resources/drivers/windows/chromedriver.exe"
   }
  mac {
      webdriver.chrome.driver = "src/test/resources/webdriver/mac/chromedriver.exe"
    }
    linux {
      webdriver.chrome.driver = "src/test/resources/webdriver/linux/chromedriver.exe"
    }
  }
}

How to add Chrome Options in Serenity

We can configure various chrome options in Serenity by adding them to a property call switches in serenity.config.

chrome {
        switches ="""--windows.size=1024,800, --start-maximized;--test-type;--no-sandbox;--ignore- 
                             certificate-errors; --headless;
                   --disable-popup-blocking;--disable-default-apps;--disable-extensions-file-access-check;
                   --incognito;--disable-infobars,--disable-gpu"""
  }

How to set Chrome Preferences in Serenity

chrome {
  switches ="""--windows.size=1024,800, --start-maximized;--test-type;--no-sandbox;--ignore-certificate-errors;
                   --disable-popup-blocking;--disable-default-apps;--disable-extensions-file-access-check;
                   --incognito;--disable-infobars,--disable-gpu"""

  preferences {
      download {
          prompt_for_download: false
          default_directory: "$TEMPDIR"
       }
  }

Congratulations!! We are able to configure various Chrome Options in Serenity.

How to embed Custom Data in Serenity Report

HOME

Serenity Reports are living documentation that contains meaningful report for each Test. It illustrated, narrative reports that document and describe what your application does and how it works.

This report can be organized by using features, stories, steps, scenarios/tests. Serenity Report shows the count of passed, failed, compromised, skipped, pending, broken, ignored, pending and manual test cases count and percentage. Serenity shows Over All Test Result, Requirements, Test Specifications & Test Result. It shows the details and overall time taken by each test step of a test case. It shows the details and overall timing took by each on all test case execution.

All the above features of Serenity Report make it a great Report. But sometimes, we want to embed data from a JSON or XML file in the Serenity Report.

Let me explain this with the help of an example. Data Driven tests are created in Serenity. We have test data mentioned in a .csv file and the test is reading the data from this file. When the report is generated, it does not show what all data are used by the Test. To overcome this situation, Serenity provides a feature which uses recordReportData(). and this can be used like this:

Path credentialsFile = Paths.get("src/test/resources/testdata/credentials.csv"); 
Serenity.recordReportData().withTitle(" User Credentials with Error Message")
                                             .fromFile(credentialsFile);

If you have the contents of the report as a String, you can pass the String directly like this:

String testData = "...";
Serenity.recordReportData().withTitle("User")
                           .andContent(testData);

Let me explain this with the help of an example. Here, I have created a Data Driven Test which is using .csv file as an input file.

@RunWith(SerenityParameterizedRunner.class)
@UseTestDataFrom(value = "testdata/credentials.csv")
public class ParameterizedTestsUsingCSV {

    private String userName;
    private String passWord;
    private String errorMessage;

    @Managed(options = "--headless")
    WebDriver driver;

    @Steps
    NavigateActions navigate;

    @Steps
    StepLoginPage loginPage;


    @TestData(columnNames = "Username, Password, ErrorMessage")
    @Test
    @Title("Login to application with invalid credential generates error message")
    public void unsuccessfulLogin() throws IOException {

        // Given
        navigate.toTheHomePage();

        // When
        loginPage.inputUserName(userName);
        loginPage.inputPassword(passWord);
        loginPage.clickLogin();

        // Then
        Serenity.reportThat("Passing invalid credentials generates error message",
                () -> assertThat(loginPage.loginPageErrorMessage()).isEqualToIgnoringCase(errorMessage));

        Path credentialsFile = Paths.get("src/test/resources/testdata/credentials.csv");
        Serenity.recordReportData().withTitle(" User Credentials with Error Message").fromFile(credentialsFile);
    }

}

Here, I have used recordReportData().withTitle() to provide a name to the button created and the data is retried using fromFile().

To locate the path of the file (credentials.csv), we have used – Path and Paths which are imported from:

import java.nio.file.Path;
import java.nio.file.Paths;

To get the complete detail about this code, refer this tutorial Data Driven Tests using CSV file in Serenity.

To generate Serenity Report, use the command like this:

mvn clean verify

The Serenity Reports are generated under target/site/serenity/Index.html.

Go to the Detailed Step Description part in Serenity Report. A button with the label “User Credentials with Error Message” will appear next to the step where you called this method:

If you click on this button, you will see your data:

Keep this in mind that this feature of Serenity is available from 1.9.16 onwards only.

Congratulation!! We have learnt a wonderful feature present in Serenity.

Explicit Wait in Serenity

HOME

In the previous tutorial, I have explained the Implicit Wait in Serenity. In this tutorial, will explain the Explicit Wait in Serenity.

What is Explicit Wait?

Explicit wait is used to wait for a specific web element on the web page for the specified amount of time. You can configure wait time element by element basis.

By deafult explicit wait is for 5 sec with an interval of 10 ms.

Below is the example where I have created two classes – ExplicitWaitDemo and SynchronizationTests.

ExplicitWaitDemo

@DefaultUrl("http://the-internet.herokuapp.com/dynamic_loading/1")
public class ExplicitWaitDemo extends PageObject {

    //Incorrect XPath
	@FindBy(xpath = "//*[@id='start']/buttons")
	WebElementFacade startButton;

	@FindBy(xpath = "//*[@id='finish']/h4")
	WebElementFacade pageText;

	public void explicitWaitDemo1() throws InterruptedException {

		open();

		startButton.waitUntilClickable().click();

	}
}

SynchronizationTests

@RunWith(SerenityRunner.class)
public class SynchronizationTests {

	ExplicitWaitDemo ewaitDemo;

	@Managed
	WebDriver driver;

	@Test
	public void waitTest1() throws InterruptedException {

		ewaitDemo.explicitWaitDemo1();

	}

}

You can see that Serenity waited for 5 sec with an interval of 100 ms.

When we need to wait for a web element for specific amount of time , then below mentioned command can be added to serenity.conf.

webdriver {
      wait {
         for {
            timeout = 6000
          
        }  
   } 
}

The same can be added to serenity.properties as shown below.

webdriver.wait.for.timeout = 6000

Now, let us run the same above test. I have used incorrect Xpath for button. So the test should fail after trying to locate the button for 6 secs.

You can print the explicit wait time by using the method – getWaitForTimeout().

In the below example, I have used the explicit wait as 6 sec and which is also returned by menthod – getWaitForTimeout().

@DefaultUrl("http://the-internet.herokuapp.com/dynamic_loading/2")
public class ExplicitWaitDemo extends PageObject {

	@FindBy(xpath = "//*[@id='start']/button")
	WebElementFacade startButton;

	@FindBy(xpath = "//*[@id='finish']/h4")
	WebElementFacade pageText;

	public void explicitWaitDemo1() throws InterruptedException {

		open();

		startButton.click();

		System.out.println("Explicit Time defined for the test (in seconds):" + getWaitForTimeout().toSeconds());

	}
}

You can override the value of explicit wait mentioned in the serenity.properties or serenity.conf files. This can be done by using method – withTimeoutOf(Duration duration).

@DefaultUrl("http://the-internet.herokuapp.com/dynamic_loading/1")
public class ExplicitWaitDemo extends PageObject {

    //Incorrect XPath
	@FindBy(xpath = "//*[@id='start']/buttons")
	WebElementFacade startButton;

	@FindBy(xpath = "//*[@id='finish']/h4")
	WebElementFacade pageText;

	public void explicitWaitDemo1() throws InterruptedException {

		open();

       //Override the value mentioned in serenity.conf for timeout from 6 sec to 8 sec
		startButton.withTimeoutOf(Duration.ofSeconds(8)).click();

	}
}

You can also wait for more arbitrary conditions, e.g.

@DefaultUrl("http://the-internet.herokuapp.com/dynamic_loading/2")
public class ExplicitWaitDemo extends PageObject {

	@FindBy(xpath = "//*[@id='start']/button")
	WebElementFacade startButton;

	@FindBy(xpath = "//*[@id='finish']/h4")
	WebElementFacade pageText;

	public void explicitWaitDemo1() throws InterruptedException {

		open();
        startButton.click();
		String expected = waitFor(pageText).getText();
		System.out.println("Value of Page :" + expected);
		Assert.assertEquals("Hello World!", expected);

	}
}

You can also specify the timeout for a field. For example, if you wanted to wait for up to 8 seconds for a button to become clickable before clicking on it, you could do the following:

startButton.withTimeoutOf(Duration.ofSeconds(8)).waitUntilClickable().click();

Finally, if a specific element a PageObject needs to have a bit more time to load, you can use the timeoutInSeconds attribute in the Serenity @FindBy annotation, e.g.

import net.serenitybdd.core.annotations.findby.FindBy;
...
@FindBy(xpath = ("//*[@id='start']/button"), timeoutInSeconds="10"))
public WebElementFacade startButton;

To wait for a specific text on the web page, you can use waitForTextToAppear attribute

waitForTextToAppear("Example 1").waitFor(startButton).click();

There are many other methods that can be used with Explicit Wait.

We are done! Congratulations on making it through this tutorial and hope you found it useful! Happy Learning!!

Implicit Wait in Serenity

HOME

Most Web applications are asynchronous by nature. So it has become necessary to wait for elements, before trying to interact with them. This can be achieved by the use of wait functionality.

I won’t recommend the use of Thread.sleep() statement to wait for a specific web element in the web page, as it slows down the execution as well as makes the test brittle. If you are using Serenity and Selenium for tests, then there are various default timeouts – Implicit, Explicit, and Fluent Wait.

What is Implicit Wait?

Implicit Waits are used to ensure that Serenity does not fail a test if a web element is not immediately present on the page when you first try to use it. Using Implicit wait, you can search for the web element for the specified amount of time. If still, the web element is not found, then Serenity throws NoSuchElementException exception.

To use ImplicitWait in the Test, mention the below-mentioned statement in serenity.properties.

webdriver.timeouts.implicitlywait

There is another way to add implicitwait. Add it to the serenity.conf file as shown below:-

webdriver {
    timeouts {
        implicitlywait = 5000
     }
}

Note:- Make sure to add webdriver and timeout in the same file, either both to properties file or both to conf files.

Let me explain the use of Implicit Wait. Below I have created two classes – ImplictWaitDemo and SynchronizationTests.

ImplictWaitDemo contains detail like default URL, XPath of web elements, methods containing the code for the test whereas SynchronizationTests class calls the tests defined in ImplictWaitDemo and run them using Serenity Runner (@RunWith(SerenityRunner.class)

Scenario 1 – The default value in Serenity for Implicit Wait is currently 2 seconds. In the below example, I’ll open a web page and will try to assert the text present in the webpage. Serenity will wait for 2 seconds and the web element will not be found in 2 secs, so the test fails.

ImplictWaitDemo

@DefaultUrl("http://the-internet.herokuapp.com/dynamic_loading/1")
public class ImplictWaitDemo extends PageObject {

	@FindBy(xpath = "//*[@id='start']/button")
	WebElementFacade startButton;

	@FindBy(xpath = "//*[@id='finish']/h4")
	WebElementFacade pageText;

	public void implictWaitDemo1() throws InterruptedException {
		open();

		startButton.click();
		Assert.assertEquals("Hello World!", pageText.getText())
	}
}

SynchronizationTests

@RunWith(SerenityRunner.class)
public class SynchronizationTests {

	ImplictWaitDemo demo;

	@Managed
	WebDriver driver;

	@Test
	public void waitTest1() throws InterruptedException {
		demo.implictWaitDemo1();

	}
}

This shows that Serenity waited for 2 sec for the text – Hello World.

Now, let us add Implicit Wait to the Test. I have added the implicitWait for 5 sec to each step.

webdriver {
    driver = firefox
    timeouts {
        implicitlywait = 5000
     }
}

Now, the Test is successful.

To know the value of wait in the code, you can use the below code. It will show the value of implicit wait in seconds or milliseconds.

System.out.println("Implicit Time defined for the test (in seconds):" + getImplicitWaitTimeout().toSeconds());
System.out.println("Implicit Time defined for the test (in milliseconds):" + getImplicitWaitTimeout().toMillis());

We are done! Congratulations on making it through this tutorial and hope you found it useful! Happy Learning!!

Data Driven Tests using CSV file in Serenity

HOME

In the previous tutorial, I have explained the Data Driven Tests in Serenity where test data are defined in Tests. In this tutorial, I will explain the Data Driven tests in Serenity where we will get the test data from CSV file.

To start with this tutorial, refer this tutorial also which explains how to setup a project with Serenity and JUnit4.

Serenity lets us perform data-driven testing using test data in a CSV file. We store our test data in a CSV file (by default with columns separated by commas), with the first column acting as a header.

We need to create a test class containing properties that match the columns in the test data, as you did for the data-driven test in the previous example. The test class will typically contain one or more tests that use these properties as parameters to the test step or Page Object methods.

Here, we need to keep in mind that as the tests are parameterized , we need to use the Parameterized test runner to perform data-driven tests.

@RunWith(SerenityParameterizedRunner.class)

This is imported from below package

net.serenitybdd.junit.runners.SerenityParameterizedRunner;

@UseTestDataFrom annotation is used to indicate where to find the CSV file (this can either be a file on the classpath or a relative or absolute file path – putting the data set on the class path (e.g. in src/test/resources) makes the tests more portable).

@UseTestDataFrom(value = "testdata/credentials.csv")

Below is the example of the Parameterized Tests.

ParameterizedTestsUsingCSV Class contains the SerenityParameterizedRunner as well as provide the path of the testdata file using @UseTestDataFrom, and the Tests.

The Serenity Parameterized Runner creates a new instance of this class for each row of data in the CSV file, assigning the properties with corresponding values in the test data. As you can see, I have mentioned 3 variables in CSV file – userName, passWord and errorMessage. I have declared the same private variables in the Test Class too – username , password and errorMessage that match the columns in the test data file. Keep this in mind, that the column name should be same in testdata file and Test.

import net.serenitybdd.core.Serenity;
import net.serenitybdd.junit.runners.SerenityParameterizedRunner;
import net.thucydides.core.annotations.Managed;
import net.thucydides.core.annotations.Steps;
import net.thucydides.core.annotations.Title;
import net.thucydides.junit.annotations.Qualifier;
import net.thucydides.junit.annotations.TestData;
import net.thucydides.junit.annotations.UseTestDataFrom;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.openqa.selenium.WebDriver;


import static org.assertj.core.api.Assertions.assertThat;

@RunWith(SerenityParameterizedRunner.class)
@UseTestDataFrom(value = "testdata/credentials.csv")
public class ParameterizedTestsUsingCSV {

    private String userName;
    private String passWord;
    private String errorMessage;

    @Managed(options = "--headless")
    WebDriver driver;

    @Steps
    NavigateActions navigate;

    @Steps
    StepLoginPage loginPage;


    @TestData(columnNames = "Username, Password, ErrorMessage")

    @Qualifier
    public String qualifier(){return " - " + " Username = " + userName + " and " + " Password = " + passWord + " should display " + errorMessage;}
    @Test
    @Title("Login to application with invalid credential generates error message")
    public void unsuccessfulLogin() {

        // Given
        navigate.toTheHomePage();

        // When
        loginPage.inputUserName(userName);
        loginPage.inputPassword(passWord);
        loginPage.clickLogin();

        // Then
        Serenity.reportThat("Passing invalid credentials generates error message",
                () -> assertThat(loginPage.loginPageErrorMessage()).isEqualToIgnoringCase(errorMessage));
    }

}

The heading of parameters present in the Serenity Report (Index.html) like Username, Password and Error Message are generated by @TestData(columnNames).

The description of the Test Step in the Serenity Report is modified by using @Qualifier. It is used to mark a method as a qualifier in an instantiated data-driven test case.

The test class needs to have a WebDriver instance with a @Managed annotation for Serenity to manage it in the background. That is all that is required, we do not need to manage the driver any more. Each test class will need this driver variable declaration.

The Test Class uses Step Class (StepLoginPage) and Action Class (NavigateActions) to perform the Tests. StepLoginPage contains test steps that represent the level of abstraction between the code that interacts with the application. NavigateAction page is used to open an environment-specific page defined in the serenity.config file under the pages section.

StepLoginPage

public class StepLoginPage extends PageObject {

    @FindBy(name = "txtUsername")
    WebElementFacade username;

    @FindBy(name = "txtPassword")
    WebElementFacade txtPassword;

    @FindBy(name = "Submit")
    WebElementFacade submitButton;

    @FindBy(id = "spanMessage")
    WebElementFacade errorMessage;

    @FindBy(xpath = "//*[@id='forgotPasswordLink']/a")
    WebElementFacade forgotPasswordLinkText;

    @Step("Enter Username")
    public void inputUserName(String userName) {
        $("[name='txtUsername']").sendKeys((userName));
    }

    @Step("Enter Password")
    public void inputPassword(String passWord) {
        txtPassword.sendKeys((passWord));
    }

    @Step("Click Submit Button")
    public void clickLogin() {
        submitButton.click();
    }

    @Step("Error Message on unsuccessful login")
    public String loginPageErrorMessage() {
        return errorMessage.getText();
    }

    @Step("Click Forget Password Link")
    public void clickForgetPasswordLink() {
        forgotPasswordLinkText.click();
    }

}

NavigateActions

public class NavigateActions extends UIInteractionSteps {

    @Step
    public void toTheHomePage() {
        openPageNamed("loginForm");
    }
}

Serenity.config

webdriver{
    driver = chrome
}

chrome {
  switches ="""--start-maximized;--test-type;--no-sandbox;--ignore-certificate-errors;
                   --disable-popup-blocking;--disable-default-apps;--disable-extensions-file-access-check;
                   --incognito;--disable-infobars,--disable-gpu"""
  }

pages{
  loginForm = "https://opensource-demo.orangehrmlive.com/"
  }

This test can be executed by JUnit as well as from command line

JUnit – Right click on the Test, select Run As and then select JUnit Test in Eclipse.

If you are using IntelliJ, then right click and select Run ‘ParameterizedTestsUsingCSV’

The Tests execution status can be seen as shown below:

To run the tests using command line, use the below command

mvn clean verify

This will execute the tests and will generate the Test Execution Report as shown below.

The reports are generated as shown in below image.

Serenity generates very descriptive and beautiful reports – Index.html and Serenity Summary Report.

Index.html

This page provide the detail about the Test, its corresponding test data, status of each test scenario with screenshots and execution time of each test.

This is the expanded view of all the test steps of a test with their screenshots. This also shows the execution time of each step in the test.

Serenity Summary Report

This report is ingle-page, self-contained HTML summary report, containing an overview of the test results, and a configurable breakdown of the status of different areas of the application.

We are done! Congratulations on making it through this tutorial and hope you found it useful! Happy Learning!!

Data Driven Tests in Serenity with JUnit

HOME

In the previous tutorial, I have explained the Testing of Web Application using Serenity with JUnit4. In this tutorial, I will explain Data Driven Tests in Serenity with JUnit4. Serenity provides features to support Data Driven tests. Refer this tutorial to know how to setup a Serenity project with JUnit4.

There is a parameterized Test Runner to perform data driven tests in JUnit4.

@RunWith(SerenityParameterizedRunner.class)

This runner is very similar to the JUnit Parameterized test runner. Here, @TestData annotation is used to provide test data to the test, and you can use all of the other Serenity annotations like (@Managed, @Steps, @Title and so on). This test runner will also generate proper serenity reports for the executed tests.

Below is an example of data-driven serenity test. In this test, I have created a Test Class (ParameterizationTests) and Step Class (StepLoginPage) and Action Class (NavigateActions). I am passing a set of incorrect credentials to the Login page and will verify the error message.

Here is the code for ParameterizationTests.

@RunWith(SerenityParameterizedRunner.class)
public class ParameterizationTests {

    private final String userName;
    private final String passWord;
    private final String errorMessage;

    @Managed(options = "--headless")
    WebDriver driver;

    @Steps
    NavigateActions navigate;

    @Steps
    StepLoginPage loginPage;

    public ParameterizationTests(String userName, String passWord, String errorMessage) {
        super();
        this.userName = userName;
        this.passWord = passWord;
        this.errorMessage = errorMessage;
    }

    @TestData(columnNames = "Username, Password, ErrorMessage")
    public static Collection<Object[]> testData() {
        return Arrays.asList(new Object[][] { { "Admin12", "", "Password cannot be empty" },
                { "", "abc12", "Username cannot be empty" }, { "_Admin1", "admin123_", "Invalid credentials" },
                { " ", " ", "Username cannot be empty" } });
    }

    @Qualifier
     public String qualifier(){return " - " + " Username = " + userName + " and " + " Password = " + passWord + " should display " + errorMessage;}
    @Test
    @Title("Login to application with invalid credential generates error message")
    public void unsuccessfulLogin() {

        // Given
        navigate.toTheHomePage();

        // When
        loginPage.inputUserName(userName);
        loginPage.inputPassword(passWord);
        loginPage.clickLogin();

        // Then
        Serenity.reportThat("Passing invalid credentials generates error message",
                () -> assertThat(loginPage.loginPageErrorMessage()).isEqualToIgnoringCase(errorMessage));
    }

}

@TestData is the annotation for a method which provides parameters to be injected into the test class constructor by Parameterized. testData() method returns an array list of objects as shown above.

The test data is injected into member variables – userName and passWord. These values are represented as instance variables in the test class, and instantiated via the constructor. These member variables are used in the test.

@Managed is annotated as a WebDriver field that is managed by the Test Runner. The Serenity Test Runner will instantiate this WebDriver before the tests start, and close it once they have all finished.

Here is the code for the StepLoginPage.

public class StepLoginPage extends PageObject {

    @FindBy(name = "txtUsername")
    WebElementFacade username;

    @FindBy(name = "txtPassword")
    WebElementFacade txtPassword;

    @FindBy(name = "Submit")
    WebElementFacade submitButton;

    @FindBy(id = "spanMessage")
    WebElementFacade errorMessage;

    @FindBy(xpath = "//*[@id='forgotPasswordLink']/a")
    WebElementFacade forgotPasswordLinkText;

    @Step("Enter Username")
    public void inputUserName(String userName) {
        $("[name='txtUsername']").sendKeys((userName));
    }

    @Step("Enter Password")
    public void inputPassword(String passWord) {
        txtPassword.sendKeys((passWord));
    }

    @Step("Click Submit Button")
    public void clickLogin() {
        submitButton.click();
    }

    @Step("Error Message on unsuccessful login")
    public String loginPageErrorMessage() {
        return errorMessage.getText();
    }

    @Step("Click Forget Password Link")
    public void clickForgetPasswordLink() {
        forgotPasswordLinkText.click();
    }
}

NavigateActions

public class NavigateActions extends UIInteractionSteps {

    @Step
    public void toTheHomePage() {
        openPageNamed("loginForm");
    }
}

There are two ways to run the tests.

  1. Run the tests as JUnit Tests. Right click on the test and select Run As ->JUnit Test.

2. Run the tests through command line using below command.

mvn clean verify

This will run the tests as well as generate the test execution reports – Index.html and serenity-emailable.html.

So, the tests are run and the reports are generated at the shown path.

Index.html

The heading of parameters present in the Serenity Report (Index.html) like Username, Password and Error Message are generated by @TestData as shown below:

@TestData(columnNames = "Username, Password, ErrorMessage")

The description of Test Step in the Serenity Report is modified by using @Qualifier.

It is used to mark a method as a qualifier in an instantiated data-driven test case.

  @Qualifier
    public String qualifier(){return " - " + " Username = " + userName + " and " + " Password = " + passWord + " should display " + errorMessage;}

Serenity-Summary.html

It is a single-page, self-contained HTML summary report, containing an overview of the test results, and a configurable breakdown of the status of different areas of the application.

We are done! Congratulations on making it through this tutorial and hope you found it useful! Happy Learning!!

How to generate Serenity Report in customized path

HOME

In the previous tutorial, I have explained the generation of Serenity Report. In this tutorial, I will explain how to generate the Serenity Report in Customized path.

Before going through this tutorial, please refer the tutorial for Serenity Report Generation.

Add outputDirectory to serenity-maven-plugin and mention the path where we want to save our Reports.

 <plugin>
       <groupId>net.serenity-bdd.maven.plugins</groupId>
       <artifactId>serenity-maven-plugin</artifactId>
       <version>${serenity.version}</version>
       <dependencies> 
            <dependency>
                 <groupId>net.serenity-bdd</groupId>
                 <artifactId>serenity-single-page-report</artifactId>
                 <version>${serenity.version}</version>
             </dependency>                
         </dependencies>
         <configuration>
               <tags>${tags}</tags>
               <reports>single-page-html</reports> 
         </configuration>
         <executions>
             <execution>
                  <id>serenity-reports</id>
                  <phase>post-integration-test</phase>
                  <goals>
                       <goal>aggregate</goal>
                  </goals>
                  <configuration>
                            <outputDirectory>C:\\Users\\Vibha\\Projects\\Vibha_Personal\\DetailedReport</outputDirectory>
                   </configuration>
              </execution>
        </executions>
  </plugin>
  .........

Execute the tests using commandline

mvn clean verify

You can see that the reports are generated at the above specified path.

Another way is to add outputDirectory detail in serenity.properties file.

serenity.project.name = Serenity and Cucumber Report Demo
serenity.outputDirectory=C:\\Users\\Reports\\SerenityReports\\SummaryReport

How to create a Serenity Report for specified tests?

Suppose we want to run a set of tests, not complete test suite and we want to get the report containing the details of only executed tests,in short a very specific report. This can be achieved by using @tags.

 Suppose you mark each test suite with a tag @E2E. So to run only the tests for the @E2E, you could run the following:

mvn clean verify -Dtags="E2E"

You will also need to configure the serenity-maven-plugin to use the tags you provide at the command line:

 <plugin>
     <groupId>net.serenity-bdd.maven.plugins</groupId>
     <artifactId>serenity-maven-plugin</artifactId>
     <version>${serenity.version}</version>
     <dependencies> 
        <dependency>
            <groupId>net.serenity-bdd</groupId>
            <artifactId>serenity-single-page-report</artifactId>
            <version>${serenity.version}</version>
        </dependency>                
      </dependencies>
        <configuration>
             <tags>${tags}</tags>
             <reports>single-page-html</reports> 
        </configuration>
        ......... 

When you run the tests with this configuration, you will get a test report with only the tests related to the @E2E tag. 

We are done! Congratulations on making it through this tutorial and hope you found it useful! Happy Learning!!

Integration of Serenity with Rest Assured

HOME

In this tutorial, I will explain the Integration of Serenity BDD with Rest Assured for the testing of RestFul API.

What is Serenity BDD?

Serenity BDD is an open-source library that aims to make the idea of living documentation a reality.

What is Rest Assured?

Rest Assured is one of the most powerful libraries for testing RESTful API using Java language. Rest-Assured is a Java-based library that is used to test RESTful Web Services. This library behaves like a headless Client to access REST web services. The rest-Assured library also provides the ability to validate the HTTP Responses received from the server. 

Pre-Requisite

  1. Java 11 installed
  2. Maven installed
  3. Eclipse or IntelliJ installed

This framework consists of:

  1. Java 11
  2. Maven – 3.8.1
  3. Serenity – 3.2.5
  4. Serenity Rest Assured – 3.2.5
  5. Rest Assured – 5.1.1
  6. JUnit – 4.13.2
  7. Maven Surefire Plugin – 3.0.0-M5
  8. Maven Failsafe Plugin – 3.0.0-M5
  9. Maven Compiler Plugin – 3.8.1

Implementation Steps

  1. Update Properties section in Maven pom.xml
  2. Add repositories and pluginRepository to Maven pom.xml
  3. Add Serenity, Serenity Rest Assured, and JUnit4 dependencies to POM.xml
  4. Update Build Section of pom.xml
  5. Create the Test Code under src/java/test
  6. Create serenity.properties file in the root of the project
  7. Run the tests through the command line which generates Serenity Reports.

Project Structure

Step 1 – Update the Properties section in Maven pom.xml
  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <serenity.version>3.2.5</serenity.version>
    <serenity.maven.version>3.2.5</serenity.maven.version>
    <junit.version>4.13.2</junit.version>
    <rest.assured.version>5.1.1</rest.assured.version>
    <json.version>20210307</json.version>
    <maven.surefire.plugin.version>3.0.0-M5</maven.surefire.plugin.version>
    <maven.failsafe.plugin.version>3.0.0-M5</maven.failsafe.plugin.version>
    <maven.compiler.plugin.version>3.8.1</maven.compiler.plugin.version>
    <maven.compiler.source>11</maven.compiler.source>
    <maven.compiler.target>11</maven.compiler.target>
  </properties>

Step 2 – Add repositories and pluginRepository to Maven pom.xml
  <repositories>
        <repository>
            <snapshots>
                <enabled>false</enabled>
            </snapshots>
            <id>central</id>
            <name>bintray</name>
            <url>https://jcenter.bintray.com</url>
        </repository>
    </repositories>
    <pluginRepositories>
        <pluginRepository>
            <snapshots>
                <enabled>false</enabled>
            </snapshots>
            <id>central</id>
            <name>bintray-plugins</name>
            <url>https://jcenter.bintray.com</url>
        </pluginRepository>
    </pluginRepositories>

Step 3 – Add Serenity, Serenity Cucumber, Serenity Rest Assured, Rest Assured, and JUnit dependencies to POM.xml
<dependencies>
   <dependency>
            <groupId>net.serenity-bdd</groupId>
            <artifactId>serenity-core</artifactId>
            <version>${serenity.version}</version>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>net.serenity-bdd</groupId>
            <artifactId>serenity-junit</artifactId>
            <version>${serenity.version}</version>
            <scope>test</scope>
        </dependency>
      
        <dependency>
            <groupId>net.serenity-bdd</groupId>
            <artifactId>serenity-screenplay-rest</artifactId>
            <version>${serenity.version}</version>
            <scope>test</scope>
        </dependency>
        
        <dependency>
            <groupId>net.serenity-bdd</groupId>
            <artifactId>serenity-rest-assured</artifactId>
            <version>${serenity.version}</version>
            <scope>test</scope>
        </dependency>
        
        <dependency>
            <groupId>io.rest-assured</groupId>
            <artifactId>rest-assured</artifactId>
            <version>${rest.assured.version}</version>
            <scope>test</scope>
        </dependency>
         
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>${junit.version}</version>
            <scope>test</scope>
        </dependency>  
  
       <dependency>
           <groupId>org.json</groupId>
           <artifactId>json</artifactId>
           <version>${json.version}</version>
        </dependency> 
        
    </dependencies>

Step 4 – Update the Build Section of pom.xml
<build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-surefire-plugin</artifactId>
                <version>${maven.surefire.plugin.version}</version>
                <configuration>
                    <skip>true</skip>
                </configuration>
            </plugin>
            <plugin>
                <artifactId>maven-failsafe-plugin</artifactId>
                <version>${maven.failsafe.plugin.version}</version>
                <configuration>
                    <includes>
                        <include>**/*.java</include>
                        <include>**/*.Test</include>
                    </includes>
                </configuration>
                <executions>
                    <execution>
                        <goals>
                            <goal>integration-test</goal>
                            <goal>verify</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>${maven.compiler.plugin.version}</version>
                <configuration>
                    <source>${maven.compiler.source}</source>
                    <target>${maven.compiler.target}</target>
                </configuration>
            </plugin>
           <plugin>
               <groupId>net.serenity-bdd.maven.plugins</groupId>
               <artifactId>serenity-maven-plugin</artifactId>
               <version>${serenity.version}</version>
               <dependencies> 
                  <dependency>
                       <groupId>net.serenity-bdd</groupId>
                       <artifactId>serenity-single-page-report</artifactId>
                       <version>${serenity.version}</version>
                  </dependency>                
               </dependencies>
               <configuration>
                   <tags>${tags}</tags>
                   <reports>single-page-html</reports> 
               </configuration>
               <executions>
                  <execution>
                      <id>serenity-reports</id>
                      <phase>post-integration-test</phase>
                      <goals>
                          <goal>aggregate</goal>
                      </goals>
                   </execution>
               </executions>
           </plugin>
        </plugins>
    </build>

Step 5 – Create the Test Code in src/java/test directory

There are 2 ways to create the same test. One approach is to have a Definition file that contains all the test code as shown below.

    
    private static final String URL = "http://dummy.restapiexample.com/api/v1";
	public Response response;

     @Test
	public void verifyValidUser() {

		response = SerenityRest.given().contentType("application/json").header("Content-Type", "application/json").when().get(URL + "/employee/" + id);

		SerenityRest.restAssuredThat(response -> response.statusCode(200)
                    .body("data.id", equalTo(1))
				    .body("data.employee_salary", equalTo(320800))
                    .body("data.employee_name", equalTo("Tiger Nixon"))
                    .body("data.employee_age", equalTo(61)).and()
				    .body("message", equalTo("Successfully! Record has been fetched.")));

	}

	@Test
	public void verifycreateUser() {
        JSONObject data = new JSONObject();

		data.put("employee_name", "Shawn Test");
		data.put("profile_image", "test.png");
		data.put("employee_age", 30);
		data.put("employee_salary", 11111);

		response = SerenityRest.given().contentType("application/json").header("Content-Type", "application/json")
				.body(data.toString()).when().post(URL + "/create");
		
      SerenityRest.restAssuredThat(response -> response.statusCode(200)
				    .body("data.employee_salary", equalTo(11111))
                    .body("data.employee_name", equalTo("Shawn Test"))
                    .body("data.employee_age", equalTo(30)).and()
				    .body("message", equalTo("Successfully! Record has been added.")));

	}

Another approach is that all tests are split into reusable blocks called “steps”. The main principle of the BDD approach is that we are trying to keep complexity to a high-level human-readable level. First of all, let’s create a separate package to keep our steps. It is always better to keep them separate as it shows which classes contain reusable components. It is better to make steps smaller. So let’s make separate reusable steps from our tests:

import static org.hamcrest.Matchers.equalTo;
import org.json.JSONObject;
import io.restassured.response.Response;
import net.serenitybdd.rest.SerenityRest;
import net.thucydides.core.annotations.Step;

public class EmployeeSteps {

	private static final String URL = "http://dummy.restapiexample.com/api/v1";
	public Response response;

	@Step("Search user by id {0}")
	public void sendUser(int id) {
		response = SerenityRest.given().contentType("application/json").header("Content-Type", "application/json")
				.when().get(URL + "/employee/" + id);

	}

	@Step("Create a new user")
	public void createUser() {

		JSONObject data = new JSONObject();

		data.put("employee_name", "Shawn Test");
		data.put("profile_image", "test.png");
		data.put("employee_age", 30);
		data.put("employee_salary", 11111);

		response = SerenityRest.given().contentType("application/json").header("Content-Type", "application/json")
				.body(data.toString()).when().post(URL + "/create");

	}

	@Step("Verify the status code {0}")
	public void verifyStatusCode(int expectedStatusCode) {
		SerenityRest.restAssuredThat(response -> response.statusCode(expectedStatusCode));
	}

	@Step("Verify the user id {0}")
	public void verifyId(int expectedId) {
		SerenityRest.restAssuredThat(response -> response.body("data.id", equalTo(expectedId)));
	}

	@Step("Verify the user name {0}")
	public void verifyName(String expectedName) {

		SerenityRest.restAssuredThat(response -> response.body("data.employee_name", equalTo(expectedName)));
	}

	@Step("Verify the user salary {0}")
	public void verifySalary(int expectedSalary) {
		SerenityRest.restAssuredThat(response -> response.body("data.employee_salary", equalTo(expectedSalary)));
	}

	@Step("Verify the user age {0}")
	public void verifyAge(int expectedAge) {
		SerenityRest.restAssuredThat(response -> response.body("data.employee_age", equalTo(expectedAge)));
	}

	@Step("Verify the message {0}")
	public void verifyMessage(String expectedMessage) {
		SerenityRest.restAssuredThat(response -> response.body("message", equalTo(expectedMessage)));
	}

}


Now our steps are ready. Let’s refactor the main class with our tests:

import org.example.SerenityWithRestAssuredDemo.steps.EmployeeSteps;
import org.junit.Test;
import org.junit.runner.RunWith;

import net.serenitybdd.junit.runners.SerenityRunner;
import net.thucydides.core.annotations.Steps;
import net.thucydides.core.annotations.Title;

@RunWith(SerenityRunner.class)
public class EmployeesTest {

	@Steps
	EmployeeSteps employeeSteps;

	@Test
	@Title("Get User")
	public void verifyValidUser() {
		employeeSteps.sendUser(1);
		employeeSteps.verifyStatusCode(200);
		employeeSteps.verifyId(1);
		employeeSteps.verifyName("Tiger Nixon");
		employeeSteps.verifyAge(61);
		employeeSteps.verifySalary(320800);
		employeeSteps.verifyMessage("Successfully! Record has been fetched.");

	}

	@Test
	@Title("Create User")
	public void createValidUser() {

		employeeSteps.createUser();
		employeeSteps.verifyStatusCode(200);
		employeeSteps.verifyName("Shawn Test");
		employeeSteps.verifyAge(30);
		employeeSteps.verifySalary(11111);
		employeeSteps.verifyMessage("Successfully! Record has been added.");

	}

}

One more important thing we added is the “@RunWith(SerenityRunner.class)” annotation on top of the class. As we have now organized our structure to meet some basic Serenity principles, we are ready to run the test using Serenity. This time (after we added the mentioned annotation) these tests will be run using the “SerenityRunner”. For that we can use exactly the same command to run our tests:

mvn clean verify

In the console, you should find printed messages for tests to start. At the same time under the target directory you can find the HTML-generated report we were talking about before:

You can open the report in any browser:

If you click on any test you should see a detailed description of the test steps:

One of the most important features of the Serenity and REST Assured integration is that by using detailed reporting, you can easily validate all requests and response details even if you are not adding any logs inside tests. Like the example above, for each executed REST request you can click the button “REST Query” and get a detailed request and response description:

There is another very useful Serenity Report – Serenity Symmary.html

As you can see, Serenity and REST Assured provide you with a wonderful combination. REST Assured keeps API testing clean and easy to maintain, while Serenity gives you outstanding test reporting and flexibility in running and grouping your tests inside a test suite.

Complete Source Code:
Refer to GitHub for source code.

We are done! Congratulations on making it through this tutorial and hope you found it useful! Happy Learning!!

Manual Tests in Serenity with JUnit5

HOME

In this tutorial, I will explain about Manual Tests in Serenity JUnit5.

You can annotate @Test not @Steps as @Manual.

In contrast to Junit4 a test method annotated with @Manual will actually be executed. This allows to further specify the example using @Step methods and show them the report.

Below is an example where tests are annotated with @Manual with description.

@SerenityTest
public class LoginTests {

	@Managed
	WebDriver driver;

	@Steps
	StepLoginPage loginPage;

	@Steps
	StepDashboardPage dashPage;

	@Steps
	StepForgetPasswordPage forgetpasswordPage;

	@Test
	@Title("Login to application should be successful")
	public void sucessfulLogin() {

		// Given
		loginPage.open();

		// When
		loginPage.inputUserName("Admin");
		loginPage.inputPassword("admin123");
		loginPage.clickLogin();

		// Then
		dashPage.loginVerify();
	}

	@Test
	@Title("Login to application should be unsuccessful with error message")
	public void unsucessfulLogin() throws InterruptedException {

		// Given
		loginPage.open();

		// When
		loginPage.inputUserName("abc");
		loginPage.inputPassword("abc12");
		loginPage.clickLogin();

		// Then
		String actualErrorMessage = loginPage.errorMessage();
		assertEquals("Invalid credentials", actualErrorMessage);
	}

	@Test
	@Manual
	void manualDefault() {
		loginPage.manualStep();
	}

	@Test
	@Manual(result = TestResult.SUCCESS)
	void manualSuccess() {
		loginPage.manualStep();
	}

	@Test
	@Manual(result = TestResult.COMPROMISED)
	void manualCompromised() {
		loginPage.manualStep();
	}

	@Test
	@Manual(result = TestResult.ERROR)
	void manualError() {
		loginPage.manualStep();
	}

	@Test
	@Manual(result = TestResult.ERROR, reason = "A reason for the error")
	void manualErrorWithReason() {
		loginPage.manualStep();
	}

	@Test
	@Manual(result = TestResult.FAILURE)
	void manualFailure() {
		loginPage.manualStep();
	}

	@Test
	@Manual(result = TestResult.IGNORED)
	void manualIgnored() {
		loginPage.manualStep();
	}

	@Test
	@Manual(result = TestResult.PENDING)
	void manualPending() {
		loginPage.manualStep();
	}

	@Test
	@Manual(result = TestResult.SKIPPED)
	void manualSkipped() {
		loginPage.manualStep();
	}

	@Test
	@Manual(result = TestResult.UNDEFINED)
	void manualUndefined() {
		loginPage.manualStep();
	}

	@Test
	@Manual(result = TestResult.UNSUCCESSFUL)
	void manualUnsuccessful() {
		loginPage.manualStep();
	}

}

StepLoginPage.java

public class StepLoginPage extends PageObject {

	@FindBy(name = "txtUsername")
	WebElementFacade username;

	@FindBy(name = "txtPassword")
	WebElementFacade password;

	@FindBy(name = "Submit")
	WebElementFacade submitButton;

	@FindBy(id = "spanMessage")
	WebElementFacade errorMessage;

	@FindBy(id = "forgotPasswordLink")
	WebElementFacade linkText;

	@Step("Enter Username")
	public void inputUserName(String userName) {
		username.sendKeys((userName));
	}

	@Step("Enter Password")
	public void inputPassword(String passWord) {
		password.sendKeys((passWord));
	}

	@Step("Click Submit Button")
	public void clickLogin() {
		submitButton.click();
	}

	@Step("Error Message on unsuccessful login")
	public String errorMessage() {
		String actualErrorMessage = errorMessage.getText();
		System.out.println("Actual Error Message :" + actualErrorMessage);
		return actualErrorMessage;
	}

	@Step("Manual Test Step")
	public void manualStep() {

		System.out.println("Verify various status of manual step");

	}

}

StepDashboardPage.java

public class StepDashboardPage extends PageObject {

	@FindBy(id = "welcome")
	WebElementFacade dashboardText;

	@Step("Successful login")
	public void loginVerify() {
		String dashboardTitle = dashboardText.getText();
		assertThat(dashboardTitle, containsString("Welcome"));
	}
}

Execute these tests by using the below command in commandline.

mvn clean verify

There are two automated tests and rest all are Manual tests. We have Manual Test marked as Default, SUCCESS, COMPROMISED, ERROR, FAILURE, IGNORED, PENDING, SKIPPED, UNDEFINED and UNSUCCESSFUL.

The execution status looks like as shown below.

The reports are generated under /target/site/serenity. There are 2 types of Reports are generated – index.html and serenity-summary.html. To know how to generate Serenity Reports, please refer tutorials for index.html and serenity-summary.html.

By default, @manual scenarios are marked as pending in the Serenity reports.

All scenarios highlighted by blue color are Pending ones whereas pink color are Broken ones.

Serenity-Summary.html

We are done! Congratulations on making it through this tutorial and hope you found it useful! Happy Learning!!