Serenity BDD with Cucumber5 for Web Application

 HOME

What is Serenity BDD?

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

Serenity BDD helps you write cleaner and more maintainable automated acceptance and regression tests faster. Serenity also uses the test results to produce illustrated, narrative reports that document and describe what your application does and how it works. Serenity tells you not only what tests have been executed, but more importantly, what requirements have been tested.

One key advantage of using Serenity BDD is that you do not have to invest time in building and maintaining your own automation framework.

Serenity BDD provides strong support for different types of automated acceptance testing, including:

  • Rich built-in support for web testing with Selenium.
  • REST API testing with Rest Assured.
  • Highly readable, maintainable and scalable automated testing with the Screenplay pattern.

Getting started Cucumber 6 with Serenity BDD

Cucumber is a popular tool for automating BDD-style acceptance criteria.

Serenity seamlessly supports for Cucumber 2.x, Cucumber 5 and Cucumber 6. However, this flexibility requires a little tweaking in the build dependencies.

If you are using Maven, you need to do the following:

  • exclude the default cucumber-core dependency from your serenity-core dependency
  • Replace your serenity-cucumber dependency with the serenity-cucumber5 dependency
  • Add dependencies on the Cucumber 5.x version of cucumber-java and cucumber-junit into your project

Serenity BDD Cucumber 5 sample scenario

Project Structure

Relationship between Web Application, Serenity BDD, Cucumber and Selenium

Scenario

  1. Launch new Browser and open – https://opensource-demo.orangehrmlive.com
  2. Enter valid Username and Password in Login Page
  3. Click on Login Button in Login Page to move to next DashBoard Page
  4. Verify DashBoard page is open
  5. Enter invalid credentials and verify the error message

Sample Feature File

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 cannot be empty        |

Lean Page Objects and Action Classes

The glue code shown below uses Serenity step libraries as action classes to make the tests easier to read, and to improve maintainability.

These classes declare using the Serenity @Steps annotation. The @Steps annotation tells Serenity to create a new instance of the class, and inject any other steps or page objects that this instance might need.

Each action class models a particular facet of user behavior: navigating to a particular page, performing a search, or retrieving the results of a search. These classes design to be small and self-contained, which makes them more stable and easier to maintain.

HomePageDefinition contains the steps to open the web browser, enter username, enter password and click on Login Button

import net.thucydides.core.annotations.Step;
import net.thucydides.core.annotations.Steps; 

public class HomePageDefinition {
     
    @Steps
	StepLoginPage loginPage;

	@Steps
	StepDashboardPage dashPage;

	@Given("User is on Home page")
	public void openApplication() {
		loginPage.open();
		System.out.println("Page is opened");
	}

	@When("User enters username as {string}")
	public void enterUsername(String userName) {
		System.out.println("Enter 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) throws InterruptedException {
		
        String actualErrorMessage = loginPage.errorMessage();
		Assert.assertEquals(expectedErrorMessage, actualErrorMessage);
	}

}

This annotation lets you define a URL or a set of URLs that work with a particular page.

@DefaultUrl("https://opensource-demo.orangehrmlive.com/")

StepHomePage is created by extending it from PageObject class. In this class,   $() method used below, which locates a web element using a By locator or an XPath or CSS expression. This class is responsible for uniquely locating elements on the page, and it does this by defining locators or occasionally by resolving web elements dynamically.

import net.serenitybdd.core.annotations.findby.By;
import net.serenitybdd.core.pages.PageObject;
import net.thucydides.core.annotations.DefaultUrl;

@DefaultUrl("https://opensource-demo.orangehrmlive.com/")
public class StepLoginPage extends PageObject {

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

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

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

	@Step("Error Message on unsuccessful login")
	public String errorMessage() {
		String actualErrorMessage = $(By.id("spanMessage")).getText();
		return actualErrorMessage;
	}

}

StepDashboardPage is also created by extending Page Object Model. Here, we are verifying the Dasboard page

import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.MatcherAssert.assertThat; 
import org.openqa.selenium.By; 
import net.serenitybdd.core.pages.PageObject; 
public class StepDashboardPage extends PageObject { 
     
     public void loginVerify() {           
         String dashboardTitle = $(By.id("welcome")).getText();            
         assertThat(dashboardTitle, containsString("Welcome"));      
     }    
} 

WebDriver Configuration

Serenity uses the serenity.conf file in the src/test/resources directory to configure test execution options. serenity.config can also contain WebDriver binaries that you need to run Selenium tests in the src/test/resources/webdriver directories. 

webdriver {
  driver = firefox
  use.driver.service.pool = false
}
drivers {
  windows {
    webdriver.chrome.driver = "src/test/resources/webdriver/window/chromedriver.exe"
    webdriver.gecko.driver = "src/test/resources/webdriver/window/geckodriver.exe"
    webdriver.ie.driver = "src/test/resources/webdriver/window/IEDriverServer.exe"
  }
  mac {
    webdriver.chrome.driver = "src/test/resources/webdriver/mac/chromedriver"
    webdriver.gecko.driver = "src/test/resources/webdriver/mac/geckodriver"
  }
  linux {
    webdriver.chrome.driver = "src/test/resources/webdriver/linux/chromedriver"
    webdriver.gecko.driver = "src/test/resources/webdriver/linux/geckodriver"
  }
 
}

Test Runner – CucumberRunnerTest

We cannot run a Feature file by its own in cucumber based framework. We need to create a Java class, which will run the Feature File. It is the starting point for JUnit to start executing the tests. TestRunner class creates under src/ test/java. When you run the tests with serenity, you use the CucumberWithSerenity test runner. If the feature files are not in the same package as the test runner class, you also need to use the @CucumberOptions class to provide the root directory where the feature files found

import org.junit.runner.RunWith; 
import io.cucumber.junit.CucumberOptions; 
import net.serenitybdd.cucumber.CucumberWithSerenity; 

@RunWith(CucumberWithSerenity.class) 
@CucumberOptions(plugin = { "pretty" }, features = "src/test/resources/features/LoginPage.feature") 

public class CucumberRunnerTest {   
} 

Executing the tests

You can run the tests from CucumberRunnerTest or from command line by

mvn clean verify

By default, the tests will run using Chrome. You can run them in Firefox by overriding the driver system property, e.g.

$ mvn clean verify -Ddriver=firefox

The test execution status looks like something this

The test report generated by Serenity places under target/site/serenity/index.html.

An example of the correctly configured dependencies shown below:

    <name>Sample Serenity BDD project using Cucumber</name>
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <serenity.version>2.2.0</serenity.version>
        <serenity.maven.version>2.2.0</serenity.maven.version>
        <serenity.cucumber.version>2.2.0</serenity.cucumber.version>
        <encoding>UTF-8</encoding>
        <tags></tags>
        <parallel.tests>4</parallel.tests>
        <webdriver.base.url></webdriver.base.url>
    </properties>
 
    <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>
 
    <dependencies>
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
            <version>1.0.13</version>
        </dependency>
        <!--
          - To use Cucumber 4, exclude the old Cucumber 2 cucumber-core dependency from the serenity-core dependency
          - and include the Cucumber 4 cucumber-java and cucumber-junit 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>net.serenity-bdd</groupId>
            <artifactId>serenity-cucumber5</artifactId>
            <version>${serenity.cucumber.version}</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-surefire-plugin</artifactId>
                <version>2.22.1</version>
                <configuration>
                    <skip>true</skip>
                </configuration>
            </plugin>
            <plugin>
                <artifactId>maven-failsafe-plugin</artifactId>
                <version>2.22.1</version>
                <configuration>
                    <includes>
                        <include>**/Test*.java</include>
                    </includes>
                    <systemPropertyVariables>
                        <webdriver.base.url>${webdriver.base.url}</webdriver.base.url>
                    </systemPropertyVariables>
                    <parallel>classes</parallel>
                    <threadCount>${parallel.tests}</threadCount>
                    <forkCount>${parallel.tests}</forkCount>
                </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>3.8.0</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin>
            <plugin>
                <groupId>net.serenity-bdd.maven.plugins</groupId>
                <artifactId>serenity-maven-plugin</artifactId>
                <version>${serenity.maven.version}</version>
                <configuration>
                  <tags>${tags}</tags>
                </configuration>
                <dependencies>
                    <dependency>
                        <groupId>net.serenity-bdd</groupId>
                        <artifactId>serenity-core</artifactId>
                        <version>${serenity.version}</version>
                    </dependency>
                </dependencies>
                <executions>
                    <execution>
                        <id>serenity-reports</id>
                        <phase>post-integration-test</phase>
                        <goals>
                            <goal>aggregate</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
</project>

4 thoughts on “Serenity BDD with Cucumber5 for Web Application

  1. Hello Vibha ,

    Kindly I am using serenity framework , i have issue with my project , can’t execute my tests via feature file , or test runner , the console doesn’t show a clear description of the error , can you share your email so i can send you screenshots of my POM ?

    Best regards

    Like

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s