Master Functional Testing in Spring Boot with Docker, WireMock, and Testcontainers

This comprehensive guide explains how to design and implement functional tests for a Spring Boot microservice using Docker containers, REST‑Assured, WireMock, and Testcontainers, covering architecture, code examples, test configuration, and best practices for maintainable black‑box testing.

Programmer DD
Programmer DD
Programmer DD
Master Functional Testing in Spring Boot with Docker, WireMock, and Testcontainers

Overview

This article demonstrates best practices for functional testing a Spring Boot microservice, focusing on black‑box testing without mocking the Spring context.

Theory

Functional testing verifies that software meets expected business requirements and produces the correct output for given inputs, ensuring usability and correctness of each feature.

Purpose

The tests should cover context startup, business requirements, and user stories, with each story having dedicated functional tests.

Practice

We build a sample service that stores and retrieves user details via a REST API and fetches contact information from an external service.

Architecture Design

The service uses Spring Boot, MariaDB, and a REST controller. The component diagram is illustrated below.

Architecture diagram
Architecture diagram

Implementation

Model classes

@Value(staticConstructor = "of")
public class UserDetails {
    String firstName;
    String lastName;
    public static UserDetails fromEntity(UserDetailsEntity entity) {
        return UserDetails.of(entity.getFirstName(), entity.getLastName());
    }
    public UserDetailsEntity toEntity(long userId) {
        return new UserDetailsEntity(userId, firstName, lastName);
    }
}

REST API

@RestController
@RequestMapping("user")
@AllArgsConstructor
public class UserController {
    private final UserService userService;
    @GetMapping("/{userId}")
    public User getUser(@PathVariable("userId") long userId) {
        return userService.getUser(userId);
    }
    @PostMapping("/{userId}/details")
    public void saveUserDetails(@PathVariable("userId") long userId,
                                @RequestBody UserDetails userDetails) {
        userService.saveDetails(userId, userDetails);
    }
    @GetMapping("/{userId}/details")
    public UserDetails getUserDetails(@PathVariable("userId") long userId) {
        return userService.getDetails(userId);
    }
}

Database

The service persists data in MariaDB, but functional tests use a real MariaDB container via Testcontainers to avoid schema differences between H2 and production databases.

Functional Testing Setup

Two communication channels are tested:

Input channel – the service’s REST API, exercised with REST‑Assured .

Output channel – the external contacts service, stubbed with WireMock .

Testcontainers provides a real MariaDB instance for the tests.

Test Steps

Step objects encapsulate API calls:

@Component
public class UserDetailsServiceSteps implements ApplicationListener<WebServerInitializedEvent> {
    private int servicePort;
    public String getUser(long userId) {
        return given().port(servicePort)
            .when().get("user/" + userId)
            .then().statusCode(200).contentType(ContentType.JSON)
            .extract().asString();
    }
    public void saveUserDetails(long userId, String body) {
        given().port(servicePort).body(body).contentType(ContentType.JSON)
            .when().post("user/" + userId + "/details")
            .then().statusCode(200);
    }
    @Override
    public void onApplicationEvent(@NotNull WebServerInitializedEvent event) {
        this.servicePort = event.getWebServer().getPort();
    }
}
@Component
public class ContactsServiceSteps {
    public void expectGetUserContacts(long userId, String body) {
        stubFor(get(urlPathMatching("/contacts"))
            .withQueryParam("userId", equalTo(String.valueOf(userId)))
            .willReturn(okJson(body)));
    }
}

Base Test Class

@RunWith(SpringRunner.class)
@SpringBootTest(classes = UserDetailsServiceApplication.class,
    webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@ActiveProfiles("test")
public abstract class BaseFunctionalTest {
    @Rule
    public WireMockRule contactsServiceMock = new WireMockRule(options().port(8777));
    @Autowired
    protected UserDetailsServiceSteps userDetailsServiceSteps;
    @Autowired
    protected ContactsServiceSteps contactsServiceSteps;
    @TestConfiguration
    @ComponentScan("com.tdanylchuk.user.details.steps")
    static class StepsConfiguration {}
}

Example Test

public class RestUserDetailsTest extends BaseFunctionalTest {
    private static final long USER_ID = 32343L;
    private final String userContactsResponse = readFile("json/user-contacts.json");
    private final String userDetails = readFile("json/user-details.json");
    private final String expectedUserResponse = readFile("json/user.json");
    @Test
    public void shouldSaveUserDetailsAndRetrieveUser() throws Exception {
        // when
        userDetailsServiceSteps.saveUserDetails(USER_ID, userDetails);
        // and
        contactsServiceSteps.expectGetUserContacts(USER_ID, userContactsResponse);
        // then
        String actualUserResponse = userDetailsServiceSteps.getUser(USER_ID);
        JSONAssert.assertEquals(expectedUserResponse, actualUserResponse, false);
    }
}

Conclusion

The guide shows how to build a Spring Boot microservice and verify its behavior with black‑box functional tests that use real Dockerized resources, ensuring tests remain reliable across environment changes and are easy to maintain.

Original source: https://dzone.com/articles/advanced-functional-testing-in-spring-boot-by-usin Author: Taras Danylchuk Translator: liumapp
Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

JavaDockerSpring BootREST APIfunctional testingTestcontainers
Programmer DD
Written by

Programmer DD

A tinkering programmer and author of "Spring Cloud Microservices in Action"

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.