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!