Enterprise Java

Building Reliable API Tests With Microcks

Modern applications usually depend on several APIs and microservices. During development and testing, however, those dependencies may not always be available. A downstream service might still be under development, require complicated test data, or be accessible only in another environment. API mocking solves this problem by providing a predictable implementation of an API contract. Instead of connecting tests to a real remote service, the application talks to a mock that behaves according to an OpenAPI or another supported API definition. Microcks provides API mocking and contract testing capabilities, while Testcontainers makes it possible to start an isolated Microcks instance directly from Java integration tests. The result is a repeatable test environment that can run locally or inside CI pipelines.

1. Overview

A typical integration test involving an external API follows a simple flow: Java Application → HTTP request → Microcks Mock API → response generated from API contract → Java Test Assertions. The Java application sends a request to the endpoint exposed by Microcks, which returns a predefined response based on the examples and rules available in the API contract. The test then validates the returned status code, response body, and other expected values. Instead of relying on a manually maintained mock server or an external service, Microcks automatically creates mock endpoints from API specifications such as OpenAPI. This makes integration tests more predictable, easier to run locally, and well suited for automated CI pipelines. This approach provides several benefits:

  • Tests do not depend on external services.
  • Mock responses are based on the API contract.
  • Developers can start testing before the real service is available.
  • Tests behave consistently on developer machines and CI servers.
  • Microcks can record and verify that mock APIs were actually invoked.
  • The same contracts can also be used for API contract testing.

1.1 What is Microcks?

Microcks is an open-source API mocking and testing platform designed around API specifications and contract-driven development. It uses API definitions and examples to automatically create realistic service simulations, allowing developers to test applications without depending on fully implemented or externally available services. Microcks can also validate real API implementations against their contracts to identify compatibility issues early in the development lifecycle. It supports several API technologies and specification formats, including REST/OpenAPI, GraphQL, gRPC, SOAP, and asynchronous APIs. There are two important Microcks capabilities to understand: API Mocking, which simulates API behavior using contract-defined examples, and API Contract Testing, which verifies that an actual API implementation behaves according to its defined specification.

1.1.1 Setting up Microcks With Testcontainers

The Microcks Testcontainers Java library starts a lightweight Microcks instance as part of the test lifecycle. Docker or another Testcontainers-compatible container runtime must therefore be available on the machine running the tests. The following Maven dependencies add Microcks Testcontainers and JUnit 5 support to the project, providing everything required to start Microcks and execute the integration test.

<dependencies>
    <dependency>
        <groupId>io.github.microcks</groupId>
        <artifactId>microcks-testcontainers</artifactId>
        <version>stable__jar__Version</version>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.junit.jupiter</groupId>
        <artifactId>junit-jupiter</artifactId>
        <version>stable__jar__version</version>
        <scope>test</scope>
    </dependency>
</dependencies>

1.2 Mocking vs. Contract Testing

It is useful to distinguish between these two use cases.

Use CasePurpose
API Mocking Simulates an API so that consumers can be developed and tested
without the real service.
Contract Testing Calls an actual API implementation and verifies that its behavior
conforms to the API contract.

2. Code Example

Suppose an application depends on a Customer API. Instead of starting the real Customer service, the test will load its OpenAPI specification into Microcks and call the generated mock endpoint.

1.1 Create the OpenAPI Contract

Before creating the Java test, define the API contract that Microcks will use to generate the mock service. The following OpenAPI definition describes the Customer API endpoint, its request parameter, response structure, and example customer data returned by the mock.

'200':
  description: Customer found
  content:
    application/json:
      schema:
        type: object
        required:
          - id
          - name
          - tier
        properties:
          id:
            type: integer
          name:
            type: string
          tier:
            type: string
      examples:
        customer:
          value:
            id: 42
            name: Ada Lovelace
            tier: GOLD
info: null
title: Customer API
version: 1.0.0
paths: null
'/customers/{id}': null
get: null
summary: Get a customer
parameters:
  - name: id
    in: path
    required: true
    schema:
      type: integer
responses: null

This OpenAPI definition describes a GET operation for the /customers/{id} endpoint of Customer API version 1.0.0. The endpoint accepts a required integer id as a path parameter and defines a successful 200 response when the requested customer is found. The response uses application/json and contains an object with three required fields: id as an integer, name as a string, and tier as a string. The examples section provides sample customer data with ID 42, name Ada Lovelace, and tier GOLD; Microcks can use this example to generate the corresponding mock API response during testing.

1.2 Start Microcks and Test the Mock API

Create CustomerApiMockTest.java to start Microcks and interact with the generated Customer API. The following JUnit test loads the OpenAPI contract, sends an HTTP request to the mock endpoint, validates the returned customer data, and verifies that Microcks received the expected invocation.

// CustomerApiMockTest.java
package com.example;

import io.github.microcks.testcontainers.MicrocksContainer;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;

import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;

class CustomerApiMockTest {

  private static MicrocksContainer microcks;
  private static HttpClient httpClient;

  @BeforeAll
  static void startMicrocks() {

    microcks = new MicrocksContainer("quay.io/microcks/microcks-uber:1.15.0").withMainArtifacts("customer-api.yaml");

    microcks.start();

    httpClient = HttpClient.newHttpClient();
  }

  @AfterAll
  static void stopMicrocks() {
    microcks.stop();
  }

  @Test
  void shouldReturnCustomerFromMockApi() throws Exception {

    // Obtain the REST endpoint generated by Microcks.
    String baseUrl = microcks.getRestMockEndpoint("Customer API", "1.0.0");

    String url = baseUrl + "/customers/42";

    HttpRequest request = HttpRequest.newBuilder().uri(URI.create(url)).GET().build();

    // Call the Microcks mock API.
    HttpResponse < String > response = httpClient.send(
    request, HttpResponse.BodyHandlers.ofString());

    // Validate the HTTP response.
    assertEquals(200, response.statusCode());
    assertTrue(response.body().contains("\"id\":42"));
    assertTrue(response.body().contains("\"name\":\"Ada Lovelace\""));
    assertTrue(response.body().contains("\"tier\":\"GOLD\""));

    // Verify that Microcks received the request.
    assertTrue(
    microcks.verify("Customer API", "1.0.0"));

    // Verify how many times the service was invoked.
    long invocationCount = microcks.getServiceInvocationsCount("Customer API", "1.0.0");

    assertEquals(1L, invocationCount);

    System.out.println("Mock endpoint: " + url);
    System.out.println("HTTP status: " + response.statusCode());
    System.out.println("Response body: " + response.body());
    System.out.println("Microcks invocations: " + invocationCount);
  }

}

1.2.1 Code Explanation

The CustomerApiMockTest class demonstrates how to test an API mock using Microcks, Testcontainers, and JUnit 5. The @BeforeAll method creates and starts a MicrocksContainer using the Microcks Uber image and imports the customer-api.yaml API specification, while Java’s HttpClient is initialized for sending requests. Inside the test, getRestMockEndpoint() retrieves the dynamically generated mock endpoint for Customer API version 1.0.0, and the test sends a GET request to /customers/42. JUnit assertions verify that the response has an HTTP 200 status and contains the expected customer details, including the ID, name, and membership tier. The verify() method confirms that Microcks received a request for the mocked service, while getServiceInvocationsCount() verifies that the service was called exactly once. Finally, the endpoint, HTTP status, response body, and invocation count are printed for visibility, and the @AfterAll method stops the Microcks container after all tests have completed.

1.2.2 Code Output

When the JUnit test is executed, Testcontainers starts the Microcks container, imports the customer-api.yaml specification, and exposes the Customer API as a mock REST endpoint. The test sends a request to the generated /customers/42 endpoint and receives the example customer response defined in the API contract. The output displays the dynamically assigned mock endpoint, HTTP status code, response body, and the number of requests recorded by Microcks. Since Testcontainers maps container ports dynamically, the port number in the mock endpoint may be different on each test run.

Mock endpoint: http://localhost:54821/rest/Customer%20API/1.0.0/customers/42
HTTP status: 200
Response body: {"id":42,"name":"Ada Lovelace","tier":"GOLD"}
Microcks invocations: 1

Tests run: 1, Failures: 0, Errors: 0, Skipped: 0

BUILD SUCCESS

The 200 status confirms that the mock request was processed successfully, while the response body contains the customer data configured in the OpenAPI example. The Microcks invocations: 1 output confirms that the mocked Customer API was called exactly once. Finally, BUILD SUCCESS indicates that all response and invocation assertions passed successfully.

3. Conclusion

Microcks and Testcontainers provide a practical approach to contract-driven API testing in Java. Microcks turns API specifications into executable mocks, while Testcontainers creates an isolated Microcks environment as part of the automated test lifecycle. In the example, an OpenAPI specification is imported into Microcks, a mock Customer API is generated automatically, a Java test calls that endpoint, and Microcks verifies that the interaction occurred. There is no separately maintained mock implementation and no dependency on an external test environment. This makes the combination particularly useful for integration tests and CI pipelines where tests need predictable API dependencies while remaining closely aligned with the API contract.

Yatin Batra

An experience full-stack engineer well versed with Core Java, Spring/Springboot, MVC, Security, AOP, Frontend (Angular & React), and cloud technologies (such as AWS, GCP, Jenkins, Docker, K8).
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Oldest
Newest Most Voted
Back to top button