API Test Automation Tutorial with Serenity BDD

RestAssured, Cucumber, Java, Maven

RoboticAutomata
4 min readFeb 24, 2024

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.

--

--