Test Smarter, Not Harder: Advanced Testing Strategies for Distributed Systems

Test Smarter, Not Harder: Advanced Testing Strategies for Distributed Systems

Modern distributed systems introduce complexity in testing. To achieve reliability without increasing effort, leverage these three proven strategies:


:white_check_mark: 1. Handle Multiple Databases with Testcontainers

Instead of managing real databases manually, Testcontainers lets you run lightweight containers for PostgreSQL, MySQL, MongoDB, and more during tests.

Java Setup

  1. Add dependency (Maven):
<dependency>
    <groupId>org.testcontainers</groupId>
    <artifactId>junit-jupiter</artifactId>
    <version>1.19.0</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.testcontainers</groupId>
    <artifactId>postgresql</artifactId>
    <version>1.19.0</version>
    <scope>test</scope>
</dependency>
  1. Start a PostgreSQL container:
import org.testcontainers.containers.PostgreSQLContainer;
import org.junit.jupiter.api.Test;

class DatabaseTest {
    @Test
    void testDatabaseConnection() {
        try (PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:15")) {
            postgres.start();
            String jdbcUrl = postgres.getJdbcUrl();
            System.out.println("Database URL: " + jdbcUrl);
        }
    }
}

:white_check_mark: 2. Mock External Services with WireMock

Use WireMock for reliable mocks of external APIs, avoiding dependency on real third-party services.

Java Setup

  1. Add dependency (Maven):
<dependency>
    <groupId>com.github.tomakehurst</groupId>
    <artifactId>wiremock-jre8</artifactId>
    <version>2.35.0</version>
    <scope>test</scope>
</dependency>
  1. Create a mock server in tests:
import static com.github.tomakehurst.wiremock.client.WireMock.*;

WireMockServer wireMockServer = new WireMockServer(8080);
wireMockServer.start();

stubFor(get(urlEqualTo("/api/test"))
    .willReturn(aResponse()
    .withStatus(200)
    .withBody("{\"status\":\"OK\"}")));

:white_check_mark: 3. Manage Async Events with Awaitility

Awaitility helps you wait for conditions in asynchronous workflows without using Thread.sleep().

Java Setup

  1. Add dependency (Maven):
<dependency>
    <groupId>org.awaitility</groupId>
    <artifactId>awaitility</artifactId>
    <version>4.2.0</version>
    <scope>test</scope>
</dependency>
  1. Use Awaitility in tests:
import static org.awaitility.Awaitility.await;
import java.util.concurrent.TimeUnit;

await().atMost(10, TimeUnit.SECONDS)
       .until(() -> someAsyncProcess.isComplete());

Full Workflow

:check_mark: Start databases with Testcontainers
:check_mark: Mock external APIs with WireMock
:check_mark: Handle async events with Awaitility


Benefits

  • Stable, Flaky-Free Tests
  • Full System Simulation
  • Scalable Test Architecture

:white_check_mark: Pro Tip: Combine these in Spring Boot Integration Tests using @Testcontainers and @SpringBootTest for real-world scenarios.

4 Likes