Hi folks, I'm back with another post.
Please check out this for previous post about Serenity.
At its core, Serenity is all about BDD.
The philosophy of Serenity is to make the test like a live documentation.
In this blog post I will share you guys how to implement UI test in Serenity with Cucumber and ScreenPlay Pattern.
And don't forget, how to create beautiful and detailed report like this:
I.Why Cucumber
Cucumber is a software tool used by computer programmers that supports behavior-driven development (BDD). Central to the Cucumber BDD approach is its plain language parser called Gherkin. It allows expected software behaviors to be specified in a logical language that customers can understand.
By using cucumber, we separate the intent of the tests from how it will be implemented.
Non-technical guys like BA or PO can easily understand what we are testing from feature file like
Feature: Allow users to login to quang cao coc coc website
@Login
Scenario Outline: Login successfully with email and password
Given Navigate to quang cao coc coc login site
When Login with '<email>' and '<password>'
Then Should navigate to home page site
Examples:
|email|password|
|xxxxxxxxxx|xxxxxxxxxx|
@Login
Scenario Outline: Login failed with invalid email
Given Navigate to quang cao coc coc login site
When Login with '<email>' and '<password>'
Then Should prompt with '<errormessage>'
Examples:
|email|password|errormessage|
|a|FernandoTorres12345#|abc@example.com|
```
II.Implementation
We will go through the needed setup for implement test using Cucumber with Serenity.
1.POM file
We would need to use serenity-cucumber for our project.
So make sure to add dependency for that:
````javascript
<!-- https://mvnrepository.com/artifact/net.serenity-bdd/serenity-cucumber -->
<dependency>
<groupId>net.serenity-bdd</groupId>
<artifactId>serenity-cucumber</artifactId>
<version>1.9.45</version>
</dependency>
```
{% endraw %}
Also we need to add some plugins to build serenity report with maven
{% raw %}
````javascript
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>8</source>
<target>8</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.0</version>
<configuration>
<testFailureIgnore>true</testFailureIgnore>
</configuration>
</plugin>
<plugin>
<artifactId>maven-failsafe-plugin</artifactId>
<version>2.18</version>
<configuration>
<includes>
<include>**/features/**/When*.java</include>
</includes>
<systemProperties>
<webdriver.driver>${webdriver.driver}</webdriver.driver>
</systemProperties>
</configuration>
</plugin>
<plugin>
<groupId>net.serenity-bdd.maven.plugins</groupId>
<artifactId>serenity-maven-plugin</artifactId>
<version>${serenity.maven.version}</version>
<executions>
<execution>
<id>serenity-reports</id>
<phase>post-integration-test</phase>
<goals>
<goal>aggregate</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
```
2.Serenity config file
In order to set the default config for serenity, we can use serenity.conf file or serenity.properties
In this example I would like to show you about serenity.conf:
````javascript
webdriver {
base.url = "https://cp.qc.coccoc.com/sign-in?lang=vi-VN"
driver = chrome
}
headless.mode=false
serenity {
project.name = "Serenity Guidelines"
tag.failures = "true"
linked.tags = "issue"
restart.browser.for.each = scenario
take.screenshots = AFTER_EACH_STEP
console.headings = minimal
browser.maximized = true
}
jira {
url = "https://jira.tcbs.com.vn"
project = Auto
username = username
password = password
}
drivers {
windows {
webdriver.chrome.driver = src/main/resources/webdriver/windows/chromedriver.exe
}
mac {
webdriver.chrome.driver = src/main/resources/chromedriver
}
linux {
webdriver.chrome.driver = src/main/resources/webdriver/linux/chromedriver
}
}
```
{% endraw %}
We defined some common thing like where to store driver for each environment:
{% raw %}
````javascript
drivers {
windows {
webdriver.chrome.driver = src/main/resources/webdriver/windows/chromedriver.exe
}
mac {
webdriver.chrome.driver = src/main/resources/chromedriver
}
linux {
webdriver.chrome.driver = src/main/resources/webdriver/linux/chromedriver
}
}
```
or take screenshot after each step:
````javacript
serenity {
take.screenshots = AFTER_EACH_STEP
}
```
{% endraw %}
3.Page Object
An experienced automation test is the one who can implement the tests in an abstracted ways for better understanding and maintenaince.
For best practices of implement test in UI, we should always define page object class for the web page we are interacting with.
In the case, the web page has a lot of functions and elements, we should separate the page object into multiple one according to the features it cover for better maintenaince.
For example with LoginPage of qcCocCoc site:
{% raw %}
````javascript
@DefaultUrl("https://cp.qc.coccoc.com/sign-in?lang=vi-VN")
public class LoginPage extends PageObject {
@FindBy(name = "email")
private WebElementFacade emailField;
@FindBy(name = "password")
private WebElementFacade passwordField;
@FindBy(css = "button[data-track_event-action='Login']")
private WebElementFacade btnLogin;
@FindBy(xpath = "//form[@method='post'][not(@name)]//div[@class='form-errors clearfix']")
private WebElementFacade errorMessageElement;
public void login(String email, String password) {
waitFor(emailField);
emailField.sendKeys(email);
passwordField.sendKeys(password);
btnLogin.click();
}
public String getMessageError(){
waitFor(errorMessageElement);
return errorMessageElement.getTextContent();
}
}
```
Here we define how to find the web element, and what method we would need to use in that page.
Usually, we should get rid of Thread.sleep , and find more fluent wait like in the example
````javascript
public void login(String email, String password) {
waitFor(emailField);
emailField.sendKeys(email);
passwordField.sendKeys(password);
btnLogin.click();
}
```
{% endraw %}
In the above, we would wait for the emailField to appear, after that, we will run the next script.
If that field does not appear, a timeout error will happen.
4.Implement the test followed Cucumber:
First you need to declare the features file.
Features file should be located in test/resources/features folder:
{% raw %}
````javascript
@Login
Scenario Outline: Login successfully with email and password
Given Navigate to quang cao coc coc login site
When Login with '<email>' and '<password>'
Then Should navigate to home page site
Examples:
|email|password|
|xxxxxxxxxx|xxxxxxxxxx|
```
IntelliJ offered us the way to automatically create the function for each step
You can click on the step and press "Alt + Enter", then follow the guide
I usually put the cucumber tests in test/ui/cucumber/qc_coccoc, and define the tests in the step package:
````javascript
public class LoginPage extends BaseTest {
@Steps
private pages.qcCocCoc.LoginPage loginPage_pageobject;
@cucumber.api.java.en.Given("^Navigate to quang cao coc coc login site$")
public void navigateToQuangCaoCocCocLoginSite() {
loginPage_pageobject.open();
}
@When("^Login with '(.*)' and '(.*)'$")
public void loginWithEmailAndPassword(String email, String password) {
loginPage_pageobject.login(email,password);
}
@Then("^Should navigate to home page site$")
public void shouldNavigateToHomePageSite() {
WebDriverWait wait = new WebDriverWait(getDriver(),2);
wait.until(ExpectedConditions.urlContains("welcome"));
softAssertImpl.assertAll();
}
@Then("^Should prompt with '(.*)'$")
public void shouldPromptWithErrormessage(String errorMessage) {
softAssertImpl.assertThat("Verify message error",loginPage_pageobject.getMessageError().contains(errorMessage),true);
softAssertImpl.assertAll();
}
}
```
{% endraw %}
Here we extend BaseTest for the benefit of using assertion
The value for email and password we put in the feature file can be gotten by using regex like @When("^Login with '(.*)' and '(.*)'$") and define input value for the function (String email, String password)
{% raw %}
````javascript
@RunWith(CucumberWithSerenity.class)
@CucumberOptions(features = "src/test/resources/features/qcCocCoc/", tags = { "@Login" }, glue = { "ui.cucumber.qc_coccoc.step" })
public class AcceptanceTest {
}
```
We should create the AcceptanceTest class for more flexible way to run tests with tags.
We need to specify the path to the features file "src/test/resources/features/qcCocCoc/", and the path to the step file: "ui.cucumber.qc_coccoc.step"
5.How to run the test
- You can run the test from feature file by right click on the scenario and choose run in the IDE
- Or you can run from command line:
mvn clean verify -Dtest=path_to_the_AcceptanceTest
6.Serenity report
To create beautiful Serenity report, just run the following command line
mvn clean verify -Dtest=path_to_the_AcceptanceTest serenity:aggregate
The test report will be index.html and located in target/site/serenity/index.html by default
The summary report will look like this:
![Alt Text](https://thepracticaldev.s3.amazonaws.com/i/zqf694axapezebrhcfm6.PNG)
With screenshot capture after each step in Test Results tab:
![Alt Text](https://thepracticaldev.s3.amazonaws.com/i/ga7v14qjt2jiyy5mivgt.PNG)
As usual, you can always checkout the sourcode from github: [serenity-guideline](https://github.com/cuongld2/serenityguideline)
Yay. That's it for today.
If you like the blog post, leave a heart or a comment.
I will write another post for screenplay pattern with UI test in a couple of days.
Take care~~
Notes: If you feel this blog help you and want to show the appreciation, feel free to drop by :
[<img src="https://thepracticaldev.s3.amazonaws.com/i/cno42wb8aik6o9ek1f89.png">](https://www.buymeacoffee.com/dOaeSPv
)
This will help me to contributing more valued contents.
Top comments (5)
Hi Cường, I love your post.I'm a tester, I begin the study of serenity. I feel the document about serenity is a little hard to understand. Can you suggest for me the way I learn and practice proficiency the serenity? Thank you so much!
Hello,
I'm really glad that my posts could help you in some way.
I feel you too. When I started learning serenity, it's a little bit hard to understand but it will get easier as you move on.
If you got the time, I strongly suggest you to checkout "BDD in action" book by Ferguson (the author of serenity). In the book, he covered the philosophy about BDD and also real world problem for Serenity to handle.
Also there are some practical example written by the serenity team, you might want to checkout : github.com/serenity-bdd/screenplay....
Happy coding!!!
Thanks a lot. I hope you have a great day :d
Hi, really appreciate your effort. I have a question, if I have 2 test suit runners AcceptanceTest1.java and AcceptanceTest2.java and I want to run only AcceptanceTest2.java by maven, how can I do this? I tried with mvn clean verify -Dtest=path_to_the_AcceptanceTest2 but it does not work. It run both of them.
Hello @duongnguyen,
I'm glad that this could help you.
Maybe you can share me the sample project you're having the issue?
From my understanding, I think it might come from configurations in the POM file.