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.

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!!

Testing of Web Application using Serenity with JUnit5

HOME

In the previous tutorial, I have explained about the Testing of Web Application using Serenity with JUnit4. In this tutorial, I’ll explain the Integration of Serenity BDD with JUnit5.

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.0
  4. Serenity JUnit5 – 3.2.0
  5. JUnit5 – 5.8.0
  6. Maven Surefire Plugin – 3.0.0-M5
  7. Maven Failsafe Plugin – 3.0.0-M5
  8. Maven Compiler Plugin – 3.8.1

Implementation Steps

  1. Update Properties section in Maven pom.xml
  2. Add Serenity , JUnit Jupiter API, JUnit Jupiter Engine and JUnit5 dependencies to POM.xml
  3. Update Build Section of pom.xml
  4. Create test code under src/test/java folder
  5. Create serenity.conf file under src/test/resources
  6. Create serenity.properties file in the root of the project
  7. Run the tests through commandline which generates Serenity Reports

Structure of Project

Step 1 – Update Properties section in Maven pom.xml

    <properties>
        <serenity.version>3.2.0</serenity.version>
        <junit5.version>5.8.2</junit5.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>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <encoding>UTF-8</encoding>
        <tags></tags>
        <webdriver.base.url></webdriver.base.url>
    </properties>

Step 2 – Add Serenity 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-junit5</artifactId>
            <version>${serenity.version}</version>
            <scope>test</scope>
        </dependency>
        <!-- JUNIT 5 DEPENDENCY-->
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter-api</artifactId>
            <version>${junit5.version}</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter-engine</artifactId>
            <version>${junit5.version}</version>
            <scope>test</scope>
        </dependency>
    </dependencies>

Step 3 – Update 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>**/*Test.java</include>
                        <include>**/Test*.java</include>
                        <include>**/*TestSuite.java</include>
                        <include>**/When*.java</include>
                    </includes>
                    <systemPropertyVariables>
                        <webdriver.base.url>${webdriver.base.url}</webdriver.base.url>
                        <junit.jupiter.extensions.autodetection.enabled>true</junit.jupiter.extensions.autodetection.enabled>
                    </systemPropertyVariables>
                </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>
                <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>
                <dependencies>
                    <dependency>
                        <groupId>net.serenity-bdd</groupId>
                        <artifactId>serenity-single-page-report</artifactId>
                        <version>${serenity.version}</version>
                    </dependency>
                </dependencies>
            </plugin>
        </plugins>
    </build>

Step 4 – Create Test Class – ApplicationLoginJUnit5Testsunder src/test/java folder

ApplicationLoginJUnit5Tests.java

import net.serenitybdd.core.Serenity;
import net.serenitybdd.junit5.SerenityJUnit5Extension;
import net.thucydides.core.annotations.Managed;
import net.thucydides.core.annotations.Steps;
import net.thucydides.core.annotations.Title;

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.openqa.selenium.WebDriver;

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

 @ExtendWith(SerenityJUnit5Extension.class)
 class ApplicationLoginJUnit5Tests {

        @Managed
        WebDriver driver;

        @Steps
        NavigateAction navigateAction;

        @Steps
        StepLoginPage loginPage;

        @Steps
        StepDashBoardPage dashboardPage;

        @Steps
        StepForgotPasswordPage forgetPasswordPage;

        @Test
        @Title("Login to application with valid credentials navigates to DashBoard page")

         void successfulLogin() {

            navigateAction.toTheHomePage();

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

            // Then
            Serenity.reportThat("Passing valid credentials navigates to DashBoard page",
                    () -> assertThat(dashboardPage.getHeading()).isEqualToIgnoringCase("DashBoard"));
        }

        @Test
        @Title("Login to application with invalid credential generates error message")
         void unsuccessfulLogin() {

            navigateAction.toTheHomePage();

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

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

        @Test
        @Title("Verify Forgot your password link")
          void clickForgetPasswordLink() {

            // Given
            navigateAction.toTheHomePage();

            // When
            loginPage.clickForgetPasswordLink();

            // Then
            Serenity.reportThat("Open Forget Password Page after clicking forget password link",
                    () -> assertThat(forgetPasswordPage.getHeadingForgetPasswordPage())
                            .isEqualToIgnoringCase("Forgot Your Password?"));
        }
    }


To run a JUnit5 test with Serenity BDD, simply add the annotation @net.serenitybdd.junit5.SerenityTest (instead of @org.junit.runner.RunWith(net.serenitybdd.junit.runners.SerenityRunner.class) for JUnit4.

@ExtendWith(SerenityJUnit5Extension.class)

@Test is imported from package:-

import org.junit.jupiter.api.Test;

StepDashboardPage.java

public class StepDashBoardPage extends PageObject {

    @FindBy(xpath = "//*[@id='content']/div/div[1]/h1")
    WebElementFacade dashboardPageTitle;

    @Step("Heading of DashBoard Page")
    public String getHeading() {
        return dashboardPageTitle.getText();


    }
}

StepForgetPasswordPage.java

public class StepForgotPasswordPage extends PageObject {


    @FindBy(xpath = "//*[@id='content']/div[1]/div[2]/h1")
    WebElementFacade forgetLink;

    @Step("Verify Forget Password Page ")
    public String getHeadingForgetPasswordPage() {

        return forgetLink.getText();
    }
}

StepLoginPage.java

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();
    }
}

The WebElementFacade class contains convenient fluent API for dealing with web elements, providing some commonly-used extra features that are not provided out-of-the-box by the WebDriver API. WebElementFacades are largely interchangeable with WebElements: you just declare a variable of type WebElementFacade instead of type WebElement

The @Steps annotation marks a Serenity step library.
Create the test following the Given/When/Then pattern and using step methods from the step library.
The @Title annotation lets you provide your own title for this test in the test reports. Serenity @Title is considered for the Serenity report.Consistently with Junit4 the @Title annotation does not influence the name in the Junit report.

The JUnit Serenity integration provides some special support for Serenity Page Objects. In particular, Serenity will automatically instantiate any PageObject fields in your JUnit test.

Junit5 @Disabled annotation can be used on test and step methods(same as @Ignore in JUnit4).

Step 5 – Create serenity.conf file under src/test/resources

Serenity.conf file is used to specify various features like type of webdriver used, various test environments, run test in headless mode and many more options.

webdriver {
  driver = chrome

}
headless.mode = true

#
# Chrome options can be defined using the chrome.switches property
#
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/"
 }

Step 6 – Create serenity.properties file in the root of the project

serenity.project.name = Serenity and JUnit5 Demo

Step 7 – Run the tests through commandline which generates Serenity Report – Index.html and Serenity-Summary.html

Execute the tests through command line by using below command

mvn clean verify

.The path of Serenity reports are mentioned in the image. The reports are generated under /target/site/serenity/.

Index.html

The detailed steps of the tests can also be viewed in the Serenity Report. It shows the execution time of all the steps in a Test.

Serenity-Summary.html

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

Integration of Serenity with Cucumber and JUnit5

HOME

In the previous tutorial, I have explained the Serenity BDD with Cucumber for Web Application using Junit4. In this tutorial, I will explain the same Test Framework using Serenity, Cucumber and JUnit5. This tutorial gives a clear picture for the initial setup of a BDD Framework .

What is JUnit5?

JUnit 5 is composed of several different modules from three different sub-projects.

JUnit 5 = JUnit Platform + JUnit Jupiter + JUnit Vintage

The JUnit Platform serves as a foundation for launching testing frameworks on the JVM. It also defines the TestEngine API for developing a testing framework that runs on the platform.

JUnit Jupiter is the combination of the new programming model and extension model for writing tests and extensions in JUnit 5. The Jupiter sub-project provides a TestEngine for running Jupiter based tests on the platform.

JUnit Vintage provides a TestEngine for running JUnit 3 and JUnit 4 based tests on the platform. It requires JUnit 4.12 or later to be present on the class/module path.

JUnit5 is not completely integrated with Serenity with Cucumber. So, it is advisable to use jupiter-vintage-engine for the Cucumber TestRunner classes.

This framework consists of:-

  1. Serenity – 3.2.3
  2. Serenity Cucumber – 3.2.3
  3. JUnit Jupiter – 5.8.0
  4. JUnit Vintage – 5.8.0
  5. Java 11
  6. Maven – 3.8.1
  7. Selenium – 3.141.59
  8. Maven Compiler Plugin – 3.8.1
  9. Maven Surefire Plugin – 3.0.0-M5
  10. Maven FailSafe Plugin – 3.0.0-M5

Implementation Steps

  1. Download and Install Java on system
  2. Download and setup Eclipse IDE on system
  3. Setup Maven and create a new Maven Project
  4. Update Properties section in Maven pom.xml
  5. Add repositories and pluginRepository to Maven pom.xml
  6. Add Serenity, Serenity Cucumber and JUnit5 dependencies to POM.xml
  7. Update Build Section of pom.xml
  8. Create a feature file under src/test/resources
  9. Create cucumber.properties file under src/test/resources (optional)
  10. Create the Step Definition class or Glue Code
  11. Create a Serenity-Cucumber Runner class
  12. Create serenity.conf file under src/test/resources
  13. Create serenity.properties file in the root of the project
  14. Run the tests from Command Line
  15. Test Execution Status
  16. Serenity Report Generation – Index.html and Serenity-Summary.html
  17. Cucumber Report Generation (Optional)

Step 1- Download and Install Java

Click here to know How to install Java.

Step 2 – Download and setup Eclipse IDE on system

The Eclipse IDE (integrated development environment) provides strong support for Java developer which is needed to write Java code. Click here to know How to install Eclipse.

Step 3 – Setup Maven and create a new Maven Project

Click here to know How to install Maven.

Click here to know How to create a Maven project

Below is the Maven project structure. Here,

Group Id – com.example
Artifact Id – SerenityCucumberJunit5Demo
Version – 0.0.1-SNAPSHOT
Package – com. example. SerenityCucumberJunit5Demo

Step 4 – Update Properties section in Maven pom.xml

  <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <serenity.version>3.2.3</serenity.version>
        <serenity.cucumber.version>3.2.3</serenity.cucumber.version>
        <junit.jupiter.version>5.8.0</junit.jupiter.version>
        <junit.vintage.version>5.8.0</junit.vintage.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>
        <maven.surefire.plugin.version>3.0.0-M5</maven.surefire.plugin.version>
        <maven.failsafe.plugin.version>3.0.0-M5</maven.failsafe.plugin.version>
  </properties>

Step 5 – 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 6 – Add Serenity, Serenity Cucumber and JUnit dependencies to POM.xml

<dependencies>

        <!-- JUnit 5 -->
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter-engine</artifactId>
            <version>${junit.jupiter.version}</version>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>org.junit.vintage</groupId>
            <artifactId>junit-vintage-engine</artifactId>
            <version>${junit.vintage.version}</version>
            <scope>test</scope>
        </dependency>

        <!-- Serenity -->
         <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-cucumber</artifactId>
            <version>${serenity.cucumber.version}</version>
            <scope>test</scope>
        </dependency>
        
        <dependency>
            <groupId>net.serenity-bdd</groupId>
            <artifactId>serenity-screenplay-webdriver</artifactId>
            <version>${serenity.version}</version>
            <scope>test</scope>
        </dependency>
             
    </dependencies>

SStep 7 – Update 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>
                    </includes>
                    <parallel>methods</parallel>
                    <useUnlimitedThreads>true</useUnlimitedThreads>
                </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>
</project>

Step 8 – Create a feature file under src/test/resources

The purpose of the Feature keyword is to provide a high-level description of a software feature, and to group related scenarios. To know more about Feature file, please refer this tutorial.

Feature: Login to HRM  

   @ValidCredentials
   Scenario: Login with valid credentials
   
    Given User is on Home page
    When User enters username as "Admin"
    And User enters password as "admin123"
    Then User should be able to login successfully
    
    @InValidCredentials    
    Scenario Outline: Login with invalid credentials
   
    Given User is on Home page
    When User enters username as '<username>'
    And User enters password as '<password>'
    Then User should be able to see error message '<errorMessage>'
      
   Examples:
    |username  |password  |errorMessage                    |
    |admin     |admin     |Invalid credentials             |
    |          |admin123  |Username cannot be empty        | 
    |Admin     |          |Password cannot be empty        |
    |          |          |Username can be empty           |
 
   @ForgetPassword  
   Scenario: Verify Forget Password Functionality
   
    Given User is on Home page
    When User clicks on Forgot your password link
    Then User should be able to see new page which contains Reset Password button

Step 9 – Create cucumber.properties file under src/test/resources (optional)

This is an optional step. Cucumber of version 6.7 and above provides the functionality to generate a beautiful cucumber report. For this, it is needed to add a file cucumber.properties under src/test/resources.

cucumber.publish.enabled = true

Step 10 – Create the Step Definition class or Glue Code

A Step Definition is a Java method with an expression that links it to one or more Gherkin steps. When Cucumber executes a Gherkin step in a scenario, it will look for a matching step definition to execute. You can have all of your step definitions in one file, or in multiple files.

LoginPageDefinitions.java

public class LoginPageDefinitions {

	@Steps
	StepLoginPage loginPage;

	@Steps
	StepDashboardPage dashPage;

	@Steps
	StepForgetPasswordPage forgetpasswordPage;

	@Given("User is on Home page")
	public void openApplication() {
		loginPage.open();
	}

	@When("User enters username as {string}")
	public void enterUsername(String userName) {
		loginPage.inputUserName(userName);
	}

	@When("User enters password as {string}")
	public void enterPassword(String passWord) {
		loginPage.inputPassword(passWord);

		loginPage.clickLogin();
	}

	@Then("User should be able to login successfully")
	public void clickOnLoginButton() {
		dashPage.loginVerify();
	}

	@Then("User should be able to see error message {string}")
	public void unsucessfulLogin(String expectedErrorMessage) {
		String actualErrorMessage = loginPage.errorMessage();
		assertEquals(expectedErrorMessage, actualErrorMessage);
	}

}

Assertions in JUnit-Vintage Engine are imported from below package:-

import static org.junit.jupiter.api.Assertions.*;

DashboardPageDefinitions.java

public class DashboardPageDefinitions {

	@Steps
	StepDashboardPage dashPage;

	@Step
	public void verifyAdminLogin() {
		dashPage.loginVerify();
	}
}

The corresponding Test Step classes are – StepLoginPage.java and StepDashboardPage.java.

There are multiple ways to identify a web element on the web page – one of the way is to use @FindBy or $(By.).

I prefer to use @FindBy as I need not to find same element multiple times. Using @FindBy , I have identified a web element and define a WebElementFacacde for the same which is reusable.

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;

	@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();
		return actualErrorMessage;
	}

}

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"));
	}
}

StepForgetPasswordPage.java

public class StepForgetPasswordPage extends PageObject {

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

	@Step("Verify Forget Password Page ")
	public boolean ForgetPasswordPage() {
		Boolean resetPasswordButton = forgetLink.isDisplayed();

		return resetPasswordButton;
	}
}

Step 11 – Create a Serenity-Cucumber Runner class

Cucumber runs the feature files via JUnit, and needs a dedicated test runner class to actually run the feature files. When you run the tests with Serenity, you use the CucumberWithSerenity test runner. You also need to use the @CucumberOptions class to provide the root directory where the feature files can be found.

import org.junit.runner.RunWith;

import io.cucumber.junit.CucumberOptions;
import net.serenitybdd.cucumber.CucumberWithSerenity;

@RunWith(CucumberWithSerenity.class)
@CucumberOptions(plugin = {}, features = "src/test/resources/features/LoginPage.feature", glue = "com.example.SerenityCucumberJunit5Demo.definitions")

public class SerenityRunnerTest {

}

Step 12 – Create serenity.conf file under src/test/resources

The serenity configuration file is used to configure the drivers so the test cases can run successfully. This file contains an operating system specific binary. The binary file sits between your test and the browser. It acts as an intermediary, an interface between your tests and the browser you are using.

You can also configure the webdriver.base.url property for different environements in the serenity.conf configuration file.

webdriver {
    driver = firefox
}

#
# Define drivers for different platforms. Serenity will automatically pick the correct driver for the current platform
#

environments {
  default {
    webdriver.base.url = "https://opensource-demo.orangehrmlive.com/"
  }
  dev {
    webdriver.base.url = "https://opensource-demo.orangehrmlive.com/dev"
  }
  staging {
    webdriver.base.url = "https://opensource-demo.orangehrmlive.com/staging"
  }
  prod {
    webdriver.base.url = "https://opensource-demo.orangehrmlive.com/prod"
  }
}

Step 13 – Create serenity.properties file in the root of the project

serenity.project.name = Serenity and Cucumber and JUnit5 Demo

Step 14 – Run the tests from Command Line

Open commandline and go to the location where pom.xml of the project is present and type the below command.

mvn clean verify

Step 15 – Test Execution Status

The image displayed above shows the execution status. There are 5 test cases passed and 1 test case failed.

The feature file contains 3 tests cases. Test Case 2 is a Test Scenario that has 4 examples. So, total we have 6 tests. This information is clearly mentioned in the new version of Serenity.

Step 16 – Serenity Report Generation

The best part about Serenity is the report generation by it. The Reports contain all possible type of information, you can think of with minimal extra effort. There are multiple type of reports are generated. We are interested in index.html and serenity-summary.html. To know more about Serenity Reports, please refer tutorials for Index.html and Serenity-Summary.html. Below is the new Serenity Report.

  1. index.html

2. serenity-summary.html

Step 17 – Cucumber Report Generation (Optional)

Every Test Execution generates a Cucumber Report (Version 6.7.0) and above as shown in the image.

Copy the url and paste to a browser and it shows the report as shown below:

To know more about Cucumber Reports, refer this tutorial.

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