Introduction

In our previous post, we explored Error Handling in REST Assured, learning how to test and manage API error responses. Now, we’ll dive into Mocking APIs, a technique to simulate API behavior for testing in controlled environments. This guide uses WireMock, a popular mocking tool, integrated with REST Assured to create mock APIs. It’s designed for beginners and experienced developers, providing clear explanations and practical examples to master API mocking.

Key Point: Mocking APIs allows you to test your application’s interaction with APIs without relying on real endpoints, enabling faster, more reliable, and isolated testing.

What is API Mocking?

API Mocking involves creating a simulated version of an API to mimic its behavior, such as returning specific responses or simulating errors. This is useful when:

  • The real API is unavailable or unstable.
  • You need to test edge cases or error scenarios.
  • You want to isolate tests from external dependencies.

REST Assured doesn’t have built-in mocking capabilities, so we use WireMock, a lightweight server that stubs HTTP responses. REST Assured then sends requests to the WireMock server and validates the mock responses.

Setting Up WireMock with REST Assured

To mock APIs, we’ll set up WireMock and use REST Assured to interact with the mock server. We’ll create examples to simulate GET, POST, and error scenarios.

Ensure your pom.xml includes dependencies for REST Assured, JUnit, and WireMock:



    
        io.rest-assured
        rest-assured
        5.4.0
        test
    
    
        org.junit.jupiter
        junit-jupiter
        5.10.2
        test
    
    
        org.hamcrest
        hamcrest
        2.2
        test
    
    
        com.github.tomakehurst
        wiremock-jre8
        3.0.1
        test
    

The wiremock-jre8 dependency provides WireMock for Java 8+ compatibility.

Mocking APIs with WireMock and REST Assured

Let’s create examples to mock API endpoints using WireMock and test them with REST Assured.

Example 1: Mocking a GET Request

Simulate a GET endpoint that returns a JSON response for a user.


import com.github.tomakehurst.wiremock.WireMockServer;
import io.restassured.RestAssured;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import static io.restassured.RestAssured.*;
import static org.hamcrest.Matchers.*;
import static com.github.tomakehurst.wiremock.client.WireMock.*;

public class MockGetTest {

    private static WireMockServer wireMockServer;

    @BeforeAll
    public static void setup() {
        wireMockServer = new WireMockServer(8080); // Start WireMock on port 8080
        wireMockServer.start();
        RestAssured.baseURI = "http://localhost:8080";

        // Configure mock response
        stubFor(get(urlEqualTo("/users/1"))
            .willReturn(aResponse()
                .withStatus(200)
                .withHeader("Content-Type", "application/json")
                .withBody("{\"id\": 1, \"name\": \"John Doe\", \"email\": \"john@example.com\"}")));
    }

    @AfterAll
    public static void teardown() {
        wireMockServer.stop();
    }

    @Test
    public void testMockGetUser() {
        given()
            .when()
                .get("/users/1")
            .then()
                .statusCode(200)
                .body("id", equalTo(1))
                .body("name", equalTo("John Doe"))
                .body("email", equalTo("john@example.com"));
    }
}

Explanation:

  • WireMockServer: Starts a mock server on port 8080.
  • stubFor: Defines a mock for a GET request to /users/1, returning a JSON response with status 200.
  • RestAssured.baseURI: Points REST Assured to the WireMock server.
  • The test validates the mock response using REST Assured’s Hamcrest matchers.
Important: Start and stop the WireMock server in @BeforeAll and @AfterAll to ensure it runs only during tests, avoiding port conflicts.

Example 2: Mocking a POST Request

Simulate a POST endpoint that accepts a JSON payload and returns a created resource.


import com.github.tomakehurst.wiremock.WireMockServer;
import io.restassured.RestAssured;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import static io.restassured.RestAssured.*;
import static org.hamcrest.Matchers.*;
import static com.github.tomakehurst.wiremock.client.WireMock.*;

public class MockPostTest {

    private static WireMockServer wireMockServer;

    @BeforeAll
    public static void setup() {
        wireMockServer = new WireMockServer(8080);
        wireMockServer.start();
        RestAssured.baseURI = "http://localhost:8080";

        // Configure mock response
        stubFor(post(urlEqualTo("/users"))
            .withRequestBody(equalToJson("{\"name\": \"Jane Doe\", \"email\": \"jane@example.com\"}"))
            .willReturn(aResponse()
                .withStatus(201)
                .withHeader("Content-Type", "application/json")
                .withBody("{\"id\": 2, \"name\": \"Jane Doe\", \"email\": \"jane@example.com\"}")));
    }

    @AfterAll
    public static void teardown() {
        wireMockServer.stop();
    }

    @Test
    public void testMockPostUser() {
        given()
            .contentType("application/json")
            .body("{\"name\": \"Jane Doe\", \"email\": \"jane@example.com\"}")
            .when()
                .post("/users")
            .then()
                .statusCode(201)
                .body("id", equalTo(2))
                .body("name", equalTo("Jane Doe"))
                .body("email", equalTo("jane@example.com"));
    }
}

Explanation:

  • stubFor(post(...)): Mocks a POST request to /users, expecting a specific JSON body.
  • withRequestBody(equalToJson(...)): Matches the incoming request body to ensure the correct payload.
  • The test sends a POST request with REST Assured and validates the mock response.

Example 3: Mocking an Error Response

Simulate an API error, such as a 400 Bad Request for invalid input.


import com.github.tomakehurst.wiremock.WireMockServer;
import io.restassured.RestAssured;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import static io.restassured.RestAssured.*;
import static org.hamcrest.Matchers.*;
import static com.github.tomakehurst.wiremock.client.WireMock.*;

public class MockErrorTest {

    private static WireMockServer wireMockServer;

    @BeforeAll
    public static void setup() {
        wireMockServer = new WireMockServer(8080);
        wireMockServer.start();
        RestAssured.baseURI = "http://localhost:8080";

        // Configure mock error response
        stubFor(post(urlEqualTo("/users"))
            .withRequestBody(equalToJson("{\"name\": \"\", \"email\": \"invalid\"}"))
            .willReturn(aResponse()
                .withStatus(400)
                .withHeader("Content-Type", "application/json")
                .withBody("{\"error\": \"Invalid input\", \"code\": \"BAD_REQUEST\"}")));
    }

    @AfterAll
    public static void teardown() {
        wireMockServer.stop();
    }

    @Test
    public void testMockBadRequest() {
        given()
            .contentType("application/json")
            .body("{\"name\": \"\", \"email\": \"invalid\"}")
            .when()
                .post("/users")
            .then()
                .statusCode(400)
                .body("error", equalTo("Invalid input"))
                .body("code", equalTo("BAD_REQUEST"));
    }
}

Explanation:

  • willReturn(aResponse().withStatus(400)): Configures WireMock to return a 400 error.
  • withBody: Provides a JSON error response.
  • The test validates the error status and message using REST Assured.
Pro Tip: Mock error responses to test how your application handles failures, ensuring robustness in real-world scenarios.

Step 1: Using Request Specification with Mocking

Use a Request Specification to centralize settings for tests interacting with the mock server.


import com.github.tomakehurst.wiremock.WireMockServer;
import io.restassured.RestAssured;
import io.restassured.builder.RequestSpecBuilder;
import io.restassured.specification.RequestSpecification;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import static io.restassured.RestAssured.*;
import static org.hamcrest.Matchers.*;
import static com.github.tomakehurst.wiremock.client.WireMock.*;

public class MockWithSpecTest {

    private static WireMockServer wireMockServer;
    private static RequestSpecification requestSpec;

    @BeforeAll
    public static void setup() {
        wireMockServer = new WireMockServer(8080);
        wireMockServer.start();
        RestAssured.baseURI = "http://localhost:8080";

        requestSpec = new RequestSpecBuilder()
            .setBaseURI("http://localhost:8080")
            .setContentType("application/json")
            .build();

        // Configure mock response
        stubFor(get(urlEqualTo("/users/1"))
            .willReturn(aResponse()
                .withStatus(200)
                .withHeader("Content-Type", "application/json")
                .withBody("{\"id\": 1, \"name\": \"John Doe\"}")));
    }

    @AfterAll
    public static void teardown() {
        wireMockServer.stop();
    }

    @Test
    public void testMockGetWithSpec() {
        given()
            .spec(requestSpec)
            .when()
                .get("/users/1")
            .then()
                .statusCode(200)
                .body("id", equalTo(1))
                .body("name", equalTo("John Doe"));
    }
}

Explanation:

  • requestSpec: Defines the base URI and content type for the mock server.
  • spec(requestSpec): Applies the specification to the test.

Step 2: Mocking Dynamic Responses

Use WireMock to return dynamic responses based on request parameters.


import com.github.tomakehurst.wiremock.WireMockServer;
import io.restassured.RestAssured;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import static io.restassured.RestAssured.*;
import static org.hamcrest.Matchers.*;
import static com.github.tomakehurst.wiremock.client.WireMock.*;

public class MockDynamicTest {

    private static WireMockServer wireMockServer;

    @BeforeAll
    public static void setup() {
        wireMockServer = new WireMockServer(8080);
        wireMockServer.start();
        RestAssured.baseURI = "http://localhost:8080";

        // Configure dynamic mock response
        stubFor(get(urlMatching("/users/\\d+"))
            .willReturn(aResponse()
                .withStatus(200)
                .withHeader("Content-Type", "application/json")
                .withBody("{\"id\": {{request.path.[1]}}, \"name\": \"User {{request.path.[1]}}\"}")));
    }

    @AfterAll
    public static void teardown() {
        wireMockServer.stop();
    }

    @Test
    public void testMockDynamicResponse() {
        given()
            .pathParam("userId", 5)
            .when()
                .get("/users/{userId}")
            .then()
                .statusCode(200)
                .body("id", equalTo("5"))
                .body("name", equalTo("User 5"));
    }
}

Explanation:

  • urlMatching("/users/\\d+"): Matches any numeric user ID in the URL.
  • {{request.path.[1]}}: WireMock template to dynamically insert the user ID from the URL into the response.
  • The test validates the dynamic response for a specific user ID.

Step 3: Verify Setup with pom.xml

Ensure your pom.xml includes all required dependencies:



    
        io.rest-assured
        rest-assured
        5.4.0
        test
    
    
        org.junit.jupiter
        junit-jupiter
        5.10.2
        test
    
    
        org.hamcrest
        hamcrest
        2.2
        test
    
    
        com.github.tomakehurst
        wiremock-jre8
        3.0.1
        test
    

Run the tests using mvn test or your IDE’s test runner to confirm the setup.

Tips for Beginners

  • Start Simple: Begin with basic GET mocks before moving to POST or dynamic responses.
  • Use WireMock Templates: Leverage WireMock’s templating for dynamic responses to reduce stub complexity.
  • Validate Thoroughly: Use REST Assured’s matchers to ensure mock responses match expected behavior.
  • Enable Logging: Use RestAssured.enableLoggingOfRequestAndResponseIfValidationFails() and WireMock’s logging to debug issues.
Troubleshooting Tip: If tests fail, ensure the WireMock server is running on the correct port and the stub matches the request (URL, method, body). Use WireMock’s admin API (/__admin/requests) to inspect unmatched requests.

What’s Next?

In the next post, we’ll cover Performance Testing, exploring how to test API performance and load using REST Assured and tools like JMeter. Stay tuned for more hands-on examples!