API Test Automation Tutorial with Serenity BDD
RestAssured, Cucumber, Java, Maven
Goals of the Blog
We will be implementing a framework to test the API’s for the Thinking Tester Contact List App (CLA). Specifically, we will be using Serenity BDD which wraps around more well known libraries, RestAssured and Cucumber, for easier reporting and setup. Our code can be found on Github.
What does the Application do?
CLA allows a user to create an account to manage contacts.
Managing contacts consists of adding, updating, viewing and deleting contacts.
Account creation consists of registering a new user, updating user information, logging in, logging out and even deleting a user (API only).
Exactly what API’s are we testing?
We will be testing the Add User, Login User and Logout User endpoints. For practice consider adding onto our framework with tests for the remaining APIs and even negative scenarios.
Add User
POST Request ->
https://thinking-tester-contact-list.herokuapp.com/users
Request Body ->
{
"firstName": "Test",
"lastName": "User",
"email": "test@fake.com",
"password": "myPassword"
}
Response Status ->
201
Login User
POST Request ->
https://thinking-tester-contact-list.herokuapp.com/users/login
Request Body ->
{
"email": "test2@fake.com",
"password": "myNewPassword"
}
Response Status ->
200
Response Body ->
{
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJfaWQiOiI2MDgyMWYzMDYyZmJiMjEzZTJhZDlhMjAiLCJpYXQiOjE2MTk3M
}
Logout User
POST Request ->
https://thinking-tester-contact-list.herokuapp.com/users/logout
Header ->
Authorization: Bearer $token
Response Status ->
200
Build the Repo
Clone or download the starter project from Serenity: serenity-rest-starter
To confirm you are setup correctly, from the root of your project, run the following in the terminal:
mvn clean verify
You should see the existing tests pass.
What are we going to build?
Our goal is to build up the starter and end up with the following project structure and files:
src/test/resources/features
└── User.feature #the test scenario
src/test/java/starter
├── apis #resquest specifications for various APIs
│ └── UserAPI.java
├── CucumberTestSuite.java #useful if running tests from an IDE
└── steps #step definitions for our feature files
└── UserSteps.java
In my case, I’ve deleted tests from the starter that aren’t relevant to our project. In your case, I would still hang on to the provided tests such that you can refer to them as a reference.
Implement the feature file
As we will be testing the user creation, login and logout endpoints, let’s create User.feature
inside src/test/features
as such:
Feature: The user endpoint works as intended
The user endpoint can handle various
Add, Get, Update, Log in, Log out
Delete requests
@dev
Scenario: Add a new user, login, logout
Given I add a new user
When I login
Then I logout
Let’s define the step definitions
The steps in our feature file don’t have any associated java code to run. To get the steps we want, let’s dry-run
User.feature
To ensure, we only run User.feature
add the tag @dev
to CucumberTestSuite.java
inside of @CucumberOptions
like so:
@CucumberOptions(
plugin = {"pretty"},
features = "classpath:features",
tags = "@dev"
)
Now to get the step definitions, inside the root of the project, run:
mvn clean verify -Dcucumber.execution.dry-run="true"
Let’s create a UserSteps.java
inside src/test/java/starter/steps
with the output we got from the terminal like so:
public class UserSteps {
@Given("I add a new user")
public void i_add_a_new_user() {
}
@When("I login")
public void i_login() {
}
@Then("I logout")
public void i_logout() {
}
}
Let’s implement Add a New User
To create random user data, we will be using java-faker
. Add the following dependency to your pom.xml
<dependency>
<groupId>com.github.javafaker</groupId>
<artifactId>javafaker</artifactId>
<version>1.0.2</version>
</dependency>
Inside, the method i_add_a_new_user
add the following to create a random firstname, lastname, email and password:
Faker user = new Faker();
FakeValuesService fakeValuesService = new FakeValuesService(Locale.ENGLISH, new RandomService());
String email = fakeValuesService.bothify("??????????#####@??????.com");
String password = fakeValuesService.bothify("?????#####");
Next, let’s store that information inside Serenity for use in later cucumber steps. Add the following:
Serenity.setSessionVariable("user").to(user);
Serenity.setSessionVariable("email").to(email);
Serenity.setSessionVariable("password").to(password);
Next, let’s setup a request body to add a user:
JsonObject requestBody = new JsonObject();
requestBody.addProperty("firstName", user.name().firstName());
requestBody.addProperty("lastName", user.name().lastName());
requestBody.addProperty("email", email);
requestBody.addProperty("password", password);
Finally, let’s make the REST request and assert the response is 201:
userAPI.addUser(requestBody);
restAssuredThat(response -> response.statusCode(201));
Additionally, we’ll need to add a reference to userAPI
inside UserSteps.java
:
public class UserSteps {
@Steps
UserAPI userAPI;
The @Steps
will be used to load userAPI
without explicit instantiation.
Let’s make User API
Inside src/test/java/starter/apis
, let’s make a UserAPI.java
with a link to our user endpoint, as well as the add user request:
public class UserAPI {
private static String USERS_ENDPOINT = "https://thinking-tester-contact-list.herokuapp.com/users/";
@Step("Add new user")
public void addUser(JsonObject requestBody) {
SerenityRest
.given()
.contentType(ContentType.JSON)
.body(requestBody.toString())
.baseUri(USERS_ENDPOINT)
.when()
.post();
}
Now, when you run mvn clean verify
you should be able to create a new user. Great!
Let’s implement the rest of User API
Now that we have the concept behind User API, let’s add the remaining endpoints and request requests (login and logout):
private static String LOGIN_ENDPOINT = "login/";
private static String LOGOUT_ENDPOINT = "logout/";
@Step("I login")
public void login(JsonObject requestBody) {
SerenityRest
.given()
.contentType(ContentType.JSON)
.body(requestBody.toString())
.baseUri(USERS_ENDPOINT)
.when()
.post(LOGIN_ENDPOINT);
}
@Step("I logout")
public void logout(String token) {
SerenityRest
.given()
.baseUri(USERS_ENDPOINT)
.header("Authorization", "Bearer " + token)
.when()
.post(LOGOUT_ENDPOINT);
}
Let’s update our User Steps
To implement the login step, we need to get our email and password from Serenity to create a request body, hit the login request, verify we got a 200 status, and store the token. Let’s implement i_login
as such:
String email =
Serenity.getCurrentSession().get("email").toString();
String password =
Serenity.getCurrentSession().get("password").toString();
JsonObject requestBody = new JsonObject();
requestBody.addProperty("email", email);
requestBody.addProperty("password", password);
userAPI.login(requestBody);
restAssuredThat(response -> response.statusCode(200));
String token =
SerenityRest.lastResponse().getBody().jsonPath().get("token");
Serenity.setSessionVariable("token").to(token);
To implement the logout step, we need to pass our token into the logout request and verify the response is 200. Let’s implement i_logout
as such:
String token = Serenity.getCurrentSession().get("token").toString();
userAPI.logout(token);
restAssuredThat(response -> response.statusCode(200));
Great, running mvn clean verify
should pass, verifying the Add User, Login and Logout functionality of the API.
Thanks for reading
Stay tuned for more UI and API Test Automation tutorials.