Serenity BDD has strong WebDriver integration and manages 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 for the user to select the WebDriver driver to the run the tests in it. The possible values are firefox, chrome, iexplorer, phantomjs, appium, safari, edge, 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 the 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 chrome driver binary can be downloaded from here – ChromeDriver – WebDriver for Chrome (chromium.org)
It is always advisable to create a driver’s directory under src/test/resources. The tests can be run on different Operating Systems, so create three sub-directories named windows, mac, and Linux within the 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.
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:
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
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.
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!!
In the previous tutorial, I have explained theData 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.
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.
@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).
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.
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.
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!!
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.
public class NavigateActions extends UIInteractionSteps {
@Step
public void toTheHomePage() {
openPageNamed("loginForm");
}
}
There are two ways to run the tests.
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:
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!!
In the previous tutorial, I explained the generation of Serenity Report. In this tutorial, I will explain how to generate the Serenity Report in Customized path.
You can see that the reports are generated at the above-specified path.
Another way is to add outputDirectorydetail 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 a complete test suite, and we want to get the report containing the details of only executed tests, in the 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:
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.
Step 5 – Create the Test Code in src/java/testdirectory
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.
public class Employee {
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!!
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!!
Step 4 – Create Test Class sunder 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)
@Testis imported from package:-
import org.junit.jupiter.api.Test;
StepDashboardPage
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
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();
}
}
The WebElementFacade class contains a 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 the type of web driver used, various test environments, run tests 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 at the root of the project
serenity.project.name = Serenity and JUnit5 Demo
Step 7 – Run the tests through the command line which generates Serenity Report
Execute the tests through the command line by using the below command
mvn clean verify
The output of the above test execution is
The path of Serenity’s reports is 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!!
In the previous tutorial, I have explained theSerenity 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.
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 usejupiter-vintage-engine for the Cucumber TestRunner classes.
This framework consists of:
Serenity – 3.2.3
Serenity Cucumber – 3.2.3
JUnit Jupiter – 5.8.0
JUnit Vintage – 5.8.0
Java 11
Maven – 3.8.1
Selenium – 3.141.59
Maven Compiler Plugin – 3.8.1
Maven Surefire Plugin – 3.0.0-M5
Maven FailSafe Plugin – 3.0.0-M5
Implementation Steps
Download and Install Javaon system
Download and setup Eclipse IDE on system
Setup Mavenand create a new MavenProject
Update Properties section in Maven pom.xml
Add repositories and pluginRepository to Maven pom.xml
Add Serenity, Serenity Cucumber and JUnit5 dependencies to POM.xml
Update Build Section of pom.xml
Create a feature file under src/test/resources
Create cucumber.properties file under src/test/resources (optional)
Create the Step Definitionclass or Glue Code
Create a Serenity-Cucumber Runner class
Create serenity.conf file under src/test/resources
Createserenity.properties file in the root of the project
Run the tests from Command Line
Test ExecutionStatus
Serenity ReportGeneration – Index.html and Serenity-Summary.html
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
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
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
public class DashboardPageDefinitions {
@Steps
StepDashboardPage dashPage;
@Step
public void verifyAdminLogin() {
dashPage.loginVerify();
}
}
The corresponding Test Step classes are – StepLoginPage and StepDashboardPage.
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
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
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
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 theCucumberWithSerenity test runner. You also need to use the @CucumberOptionsclass 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.htmland 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.
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, referthis tutorial.
We are done! Congratulations on making it through this tutorial and hope you found it useful! Happy Learning!!