NashTech Blog

API Testing with Testcontainers and REST Assured

Table of Contents

In this blog, we’ll explore how to set up containerized API testing using Testcontainers and REST Assured. We will walk through the entire process, including installing Docker, creating a mock server container, and writing test cases in Java.

What is RestAssured?

RestAssured is a Java library specifically designed for testing and validating RESTful APIs. It simplifies the process of making HTTP requests and checking the responses in an intuitive, readable way. Here’s a quick overview of what it does and how it’s typically used:

How RestAssured is Typically Used

  1. Setting Up Requests: Configure details such as base URI, headers, and authentication.
  2. Sending Requests: Use methods like .get(), .post(), etc., to send requests to the API.
  3. Validating Responses: Check status codes, headers, and contents of the response body using fluent validation methods.

What are Test Containers?

Testcontainers is a Java library for running Docker containers in integration tests, enabling realistic, isolated testing environments. It lets you spin up temporary instances of databases, web servers, or message brokers right from your test code. Each container is automatically started and stopped, so tests run in clean environments every time.

What is Docker and Why Do We Containerize?

Docker is a platform design and an open- source that allows automating different things like deployment, scaling, and management of applications using containerization. It provides an additional layer of abstraction and isolation, enabling you to package an application along with its dependencies, libraries, and configuration files into a standardized unit called a container.

A container is an environment for your code. Basically, a container has no knowledge of your OS(operating system as well as your files) It runs on the environment provided by Docker and it is a lightweight and standalone executable package that contains everything needed to run an application, including the code, runtime, system tools, system libraries, and settings. It encapsulates the application and its dependencies, ensuring consistency and portability across different computing environments, such as development machines, testing environments, and production servers.

What You’ll Learn

  1. Installing Docker on your local machine.
  2. Setting up a Maven project with the necessary dependencies.
  3. Using Testcontainers to create a mock server container.
  4. Writing and executing REST Assured tests.
  5. Managing the lifecycle of containers in your test suite.

Prerequisites

  • Java Development Kit (JDK) 11 or higher.
  • Maven (for dependency management).
  • An IDE (like IntelliJ IDEA or Eclipse).
  • Basic knowledge of REST APIs and Docker.

Step 1: Installing Docker

For Linux:

  1. Install Docker: Open a terminal and run:
    • sudo apt update
    • sudo apt install docker.io
  2. Start Docker:
    • sudo systemctl start docker
    • sudo systemctl enable docker
  3. Verify Installation:
    • docker --version

Step 2: Setting Up Your Maven Project for API Testing with Testcontainers

Create a new Maven project. Update your pom.xml to include dependencies for Testcontainers and REST Assured.



    <dependencies>
        <!-- JUnit Jupiter -->
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter-api</artifactId>
            <version>5.7.1</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter-engine</artifactId>
            <version>5.7.1</version>
            <scope>test</scope>
        </dependency>

        <!-- Testcontainers -->
        <dependency>
            <groupId>org.testcontainers</groupId>
            <artifactId>junit-jupiter</artifactId>
            <version>1.16.0</version>
        </dependency>
        <dependency>
            <groupId>org.testcontainers</groupId>
            <artifactId>testcontainers</artifactId>
            <version>1.16.0</version>
        </dependency>

        <!-- REST Assured for API testing -->
        <dependency>
            <groupId>io.rest-assured</groupId>
            <artifactId>rest-assured</artifactId>
            <version>4.3.3</version>
            <scope>test</scope>
        </dependency>

Step 3: API Testing on Test Containers using Test Assured Execution Flow

1.BaseTest.java

  • Purpose: Base class with setup/teardown logic for tests.
  • Execution: Sets up the environment before any tests, starting/stopping containers if needed.
package com.example;

import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;

public class BaseTest {

    protected ContainerManager containerManager;

    @BeforeEach
    public void setUp() {
        containerManager = new ContainerManager();
        // Start a container with default values; can be overridden in specific tests
        containerManager.startContainer("nginx:latest", 80);
    }

    @AfterEach
    public void tearDown() {
        containerManager.stopContainer();
    }
}

2.ContainerManager.java

  • Purpose: Manages the Docker container lifecycle (starting/stopping).
  • Execution: Initializes the container (e.g., pulls and starts the Docker image) before tests in AppTest.java.
package com.example;

import org.testcontainers.containers.GenericContainer;
import org.testcontainers.utility.DockerImageName;

public class ContainerManager {

    private GenericContainer<?> container;

    public void startContainer(String imageName, int exposedPort) {
        container = new GenericContainer<>(DockerImageName.parse(imageName))
            .withExposedPorts(exposedPort);
        container.start();
    }
    

    public String getContainerHost() {
        return container.getHost();
    }

    public Integer getContainerPort(int exposedPort) {
        return container.getMappedPort(exposedPort);
    }

    public void stopContainer() {
        if (container != null) {
            container.stop();
        }
    }
}

3.AppTest.java

  • Purpose: Main test class with defined test cases.
  • Execution:
    • @BeforeAll starts the Nginx container.
    • Test methods (e.g., testServerHeader) run in sequence, sending requests to Nginx and making assertions.
    • @AfterAll stops the container, cleaning up.
package com.example;

import org.junit.jupiter.api.Test;
import org.testcontainers.containers.GenericContainer;
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;

import static io.restassured.RestAssured.given;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.lessThan;
import static org.junit.jupiter.api.Assertions.assertTrue;

@Testcontainers
public class AppTest {

    @Container
    public GenericContainer<?> container = new GenericContainer<>("nginx:latest")
            .withExposedPorts(80);

    private String getContainerUrl() {
        Integer mappedPort = container.getMappedPort(80);
        return "http://" + container.getHost() + ":" + mappedPort;
    }

    @Test
    public void testContainerRunning() {
        String containerAddress = getContainerUrl();

        given()
                .when()
                .get(containerAddress)
                .then()
                .statusCode(200)
                .body(containsString("Welcome to nginx!")); // Check welcome message
    }

    @Test
    public void testContainerStatusCode() {
        String containerAddress = getContainerUrl();

        given()
                .when()
                .get(containerAddress)
                .then()
                .statusCode(200); // Check status code
    }

    @Test
    public void testContentType() {
        String containerAddress = getContainerUrl();

        given()
                .when()
                .get(containerAddress)
                .then()
                .contentType("text/html"); // Updated to match actual content type
    }

    @Test
    public void testResponseTime() {
        String containerAddress = getContainerUrl();

        given()
                .when()
                .get(containerAddress)
                .then()
                .time(lessThan(5000L)); // Response time should be less than 5 seconds
    }

    @Test
    public void testServerHeader() {
        String containerAddress = getContainerUrl();

        given()
                .when()
                .get(containerAddress)
                .then()
                .header("Server", "nginx/1.27.1"); // Updated to match actual server version
    }

    @Test
    public void testNotFound() {
        String containerAddress = getContainerUrl();
        String invalidEndpoint = containerAddress + "/invalid-path"; // Use a clearly invalid path

        given()
                .when()
                .get(invalidEndpoint)
                .then()
                .statusCode(404); // Check for not found status
    }

    @Test
    public void testPageTitle() {
        String containerAddress = getContainerUrl();

        String response = given()
                .when()
                .get(containerAddress)
                .asString();

        // Check if the response body contains the page title
        assertTrue(response.contains("<title>Welcome to nginx!</title>"));
    }
}

4.ApiRequestHelper.java

  • Purpose: Utility class for reusable API request methods.
  • Execution: Called by AppTest to simplify sending requests to the container without duplicating request code.
package com.example;

import  io.restassured.RestAssured;
import  io.restassured.response.Response;

public class ApiRequestHelper {

    public static Response sendGetRequest(String url) {
        return RestAssured.given().when().get(url);
    }

    public static Response sendPostRequest(String url, Object body) {
        return RestAssured.given()
                .contentType("application/json")
                .body(body)
                .when()
                .post(url);
    }

    public static Response sendPutRequest(String url, Object body) {
        return RestAssured.given()
                .contentType("application/json")
                .body(body)
                .when()
                .put(url);
    }

    public static Response sendDeleteRequest(String url) {
        return RestAssured.given().when().delete(url);
    }
}

Step 5: Running the Test cases on API Testing with Testcontainers and REST Assured

To execute your tests, run the following command in your project directory:

mvn clean test

Step 6: Observing the Results

After running the command, you should see the output of your tests in the console. Each of your test cases will indicate whether they passed or failed.

Managing Container Lifecycle

Testcontainers handles the lifecycle of your containers automatically. Once the tests are completed, Testcontainers will stop and remove the containers, ensuring a clean slate for the next test run.

Conclusion

This guide provided a comprehensive overview of setting up containerized API testing using Testcontainers and REST Assured. By following the steps outlined, you can easily create a mock server environment, write robust tests, and ensure your APIs function as expected.

References

https://testcontainers.com/guides/testing-spring-boot-rest-api-using-testcontainers/#:~:text=Implement%20a%20REST%20API%20endpoint%20using

Picture of vishnuvishwakarma

vishnuvishwakarma

Leave a Comment

Your email address will not be published. Required fields are marked *

Suggested Article

Scroll to Top