How to configure Junit in Intellij

HOME

In this tutorial we will discuss to create a JUnit  project using IntelliJ. We will be at first creating a simple Java Project and will add JUnit5 as well as create a Maven Project, then we will add a basic Class and a JUnit Test for it.

Java Project

Step 1 – Create a new Java Project.

To create a new Java project in Intellij, please refer to this tutorial.

Step 2 – Right click on the project and select Open Module Settings.

Step 3 – Go to the “Libraries” group, click the little plus (look up), and choose “From Maven…” option.

Step 4 – Search for “junit” — something like “junit:junit-4.13“. Click the “OK” button.

Step 5 – A new dialog will appear to confirm that “junit:junit:4.13.2” will be added to the module. Click the “OK” button.

Step 6 – This screens shows that junit:junit:4.13.2 is added to the Libraries. It contains the highlighted classes – junit-4.13.2.jar and hamcrest-core-1.3.jar. Click the “OK” button.

Step 7 – This image shows that the Junit is added to the External Libraries.

Step 8 – Create a Java Class – JUnit4Test under src and create a JUnit test to verify that it is installed properly.

import org.junit.Assert;
import org.junit.Test;

public class JUnit4Test {

    @Test
    public void Test() {

        String str1 = "Happy";
        String str2 = new String("Happy");
        Assert.assertEquals("String1 and String 2 are equal",str1, str2);

    }
}

Step 9 – There are many ways to run the test. One of the way is to Right-Click and select Run JUnit4Test

The successful execution of the test shows that the JUnit is configured properly.

Maven Project

Add Junit dependency to the POM.xml and build the project.

<dependencies>

    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.13.2</version>
        <scope>test</scope>
    </dependency>
    
</dependencies>

Now we need to apply the changes in the build script. Press Ctrl+Shift+O or click Load Maven Changes in the notification that appears in the top-right corner of the editor.

Create a Java Class – JUnit4Test under src/test/java and create a JUnit test to verify that it is installed properly.

import org.junit.Test;

import static org.junit.Assert.assertArrayEquals;

public class JUnitMavenTest {

    @Test
    public void Test() {

        String[] expected = {"happy","days","summer","spring"};
        String[] actual = {"happy","days","summer","spring"};

        assertArrayEquals("Expected and Actual Arrays are not equal",expected,actual);

    }
}

Similarly, to add JUnit5 we can add below mentioned dependencies to the POM.xml.

<dependency>
     <groupId>org.junit.jupiter</groupId>
     <artifactId>junit-jupiter-engine</artifactId>
     <version>5.8.2</version>
     <scope>test</scope>
</dependency>

<dependency>
     <groupId>org.junit.jupiter</groupId>
     <artifactId>junit-jupiter-api</artifactId>
     <version>5.8.2</version>
     <scope>test</scope>
</dependency>

Congratulations. We are able to add JUnit to Java or Maven project. Happy Learning!!

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

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

Testing of Web Application using Serenity with JUnit4

HOME

In the previous tutorial, I have explained Serenity BDD with Cucumber for Web Application. In this tutorial, I will explain the Integration of Serenity with JUnit4. This tutorial gives an idea of how to set up a new project where we like to use Serenity as BDD Framework and JUnit as a Testing framework.

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 – 2.6.0
  4. Serenity Maven – 2.6.0
  5. JUnit – 4.13.2
  6. Maven Surefire Plugin – 3.0.0-M5
  7. Maven Failsafe Plugin – 3.0.0-M5
  8. Maven Comiler 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 and JUnit dependencies to POM.xml
  4. Update Build Section of pom.xml
  5. Create test code under src/test/java folder
  6. Create NavigateActions class under src/test/java
  7. Create Page Object Classes – StepLoginPage, StepDashBoardPage and StepForgetPasswordPage under src/test/java
  8. Create serenity.conf file under src/test/resources
  9. Create serenity.properties file in the root of the project
  10. Run the tests through commandline which generates Serenity Report

Project Structure

This project consists of various classes – ApplicationLoginTests (This is the Test Class which is going to contain all the tests). NavigationActions is the Action class that is used to open the webpage or application. StepLoginPage, StepDashboardPage, and StepForgotPasswordPage are the Page Object classes that contain multiple functionalities of that page and that helps to keep the code clean.

Step 1 – Update Properties section in Maven pom.xml

 <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <serenity.version>2.6.0</serenity.version>
    <serenity.maven.version>2.6.0</serenity.maven.version>
    <junit.version>4.13.2</junit.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 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</artifactId>
            <version>${serenity.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>

        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>${junit.version}</version>
            <scope>test</scope>
        </dependency>      
</dependencies>

Step 4 – 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 5 – Create Test Class – ApplicationLoginTests under src/test/java folder

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

import org.junit.Test;
import org.junit.runner.RunWith;
import org.openqa.selenium.WebDriver;

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

@RunWith(SerenityRunner.class)
public class ApplicationLoginTests {

	@Managed
	WebDriver driver;

	@Steps
	NavigateActions navigate;

	@Steps
	StepLoginPage loginPage;

	@Steps
	StepDashBoardPage dashboardPage;

	@Steps
	StepForgotPasswordPage forgetPasswordPage;

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

	public void successfulLogin() {

		// Given
		navigate.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")
	public void unsuccessfulLogin() {

		// Given
		navigate.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")
	public void clickForgetPasswordLink() {

		// Given
		navigate.toTheHomePage();

		// When
		loginPage.clickForgetPasswordLink();

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

  1. The tests run using the Serenity test runner – @RunWith(SerenityRunner.class).
  2. The @Steps annotation marks a Serenity step library.
  3. Create the test following the Given/When/Then pattern and using step methods from the step library.
  4. The @Title annotation lets you provide your own title for this test in the test reports.

Step 6 – Create NavigateActions class under src/test/java

This class is used to open a web browser with the URL specified. This class is extended from UIInteractionSteps.

openPageNamed() method open an environment-specific page defined in the serenity.conf file under the pages section. The value of loginForm is derived from serenity.config:

public class NavigateActions extends UIInteractionSteps {

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

        openPageNamed("loginForm");
    }
}

serenity.config (partial config file)

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

Step 7 – Create Page Object Classes – StepLoginPage, StepDashBoardPage and StepForgetPasswordPage under src/test/java

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

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

Keep in mind to use @FindBy annotation from:

import org.openqa.selenium.support.FindBy;

PageObject class is imported from:

import net.thucydides.core.pages.PageObject;

The JUnit Serenity integration provides some special support for Serenity Page Objects. In particular, Serenity will automatically instantiate any PageObject fields in the JUnit test. When a field of type StepLoginPage is declared in the test, Serenity instantiates it for you. The page is automatically instantiated and ready to be used.

@Managed                                                                
WebDriver driver;

@Managed declares a WebDriver instance that will be managed by Serenity. The WebDriver instance will be initialized automatically.

The driver parameter lets you define what WebDriver driver you want to run these tests in. Possible values include Firefox, chrome, iexplorer, phantomjs, appium, safari, edge, and htmlunit.  The default browser in Serenity is Firefox. There are multiple ways to configure webDriver. One of the ways is to mention with @Managed as shown below:

@Managed(driver="chrome")

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

Serenity.conf file is used to specify various features like the type of webdriver used, various test environments, run test in headless mode, and many more options. Serenity.config can also contain settings like start size, disable sandbox, disable gpu, and others that need to be added to the chrome.switches setting. 

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/"
  }

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

serenity.project.name = Serenity and Junit Demo

Step 10 – Run the tests through commandline which generates Serenity Report

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

mvn clean verify

Below is the execution status.

There is 2 types of reports are generated – Index.html and Serenity-Summary.html

  1. Index.html

 We can see the value of the @Title annotation, ‘Login to application with valid credentials navigates to DashBoard page’, added as the heading. The value of @Step annotation, ‘Enter Username’, ‘Enter Password’ is added to the Report as various steps.

This report contains a screenshot of each step also.

2. Emailable Report (Serenity-Summary.html)

These reports are present under /target/site/serenity.

Skipping the tests

In Serenity, you use the @Pending annotation, either for a test or for a @Step-annotated method, to indicate that the scenario is still being implemented and that the results are not available yet. These tests appear as ‘Pending’ (shown in blue) in the test reports.

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

		// Given
		loginPage.open();

		// When
		loginPage.clickForgetPasswordLink();

		// Then
		Assert.assertTrue(forgetpasswordPage.ForgetPasswordPage());
	}

Tests marked with @Ignore will appear as ‘Ignored’ (from JUnit) appears as grey in the test reports.

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

How to manage screenshots in Serenity Report

HOME

Serenity provides wide range of options to manage screenshots in the report. By default, Serenity has set option serenity.take.screenshots=BEFORE_AND_AFTER_EACH_STEP, which means screenshot is saved for before and after each step as shown in the below image. Before this tutorial, refer the previous tutorial on How to generate Serenity Report.

However, recording a large number of screenshots can slow down test execution. So, may be we like to record the screenshot of failed steps in the scenario. To achieve this flexibility, configure serenity.take.screenshots property in serenity.properties file.

There are various other type of options for managing screenshot in Serenity Report. This property can take the following values:

  1. FOR_EACH_ACTION: Saves a screenshot at every web element action (like click(), typeAndEnter(), type(), typeAndTab() etc.).
  2. BEFORE_AND_AFTER_EACH_STEP: Saves a screenshot before and after every step.
  3. AFTER_EACH_STEP: Saves a screenshot after every step
  4. FOR_FAILURES: Saves screenshots only for failing steps.
  5. DISABLED: Doesn’t save screenshots for any steps.

In the below option, I have used FOR_FAILURES option in serenity.properties file.

serenity.project.name = Serenity and Cucumber Report Demo
current.target.version = sprint-1
serenity.take.screenshots = FOR_FAILURES

Below is the screenshot of passed test case. We can see that there is no screenshot attached to any of the test step.

Below is the screenshot of failed test case. We can see that there is a screenshot attached to the failed test step only, not all the test steps. In the below example, it is a scenario outline with four different test data. Out of four, only one set of test data has failed. So, the screenshot is generated for the failed step of that particular test data.

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