Setup Java Spring Boot Testcontainers
| This documentation was generated with the assistance of AI. Please report any inaccuracies. |
Gives a Spring Boot service one definition of its runtime dependencies, used by both mvn verify and a developer
running the application locally.
Purpose
The single most valuable property of this setup is that the two paths cannot drift: the integration tests start
the same compose.yaml a developer starts, so "works on my machine" and "passes in CI" converge by construction.
iru-setup-java-springboot-testcontainers writes that compose file, the Testcontainers ComposeContainer harness
that launches it, and the Failsafe/JaCoCo wiring that runs the tests against it.
Two details it treats as non-negotiable, both because they are the usual causes of flaky integration suites:
-
Every image tag is pinned — never
latest. An unpinned tag turns a passing suite into a time bomb that fails on an unrelated day for an unrelated reason. Resolved tags are written back intospringboot-stack.ymlso the compose file, the tests, and the OpenTofu engine versions all agree on one number. -
Every service has a real
healthcheck, not a sleep. BothComposeContainerand Spring’s own compose support wait for health, so a service without one is considered ready the instant its container starts — and tests then fail intermittently against a database that hasn’t finished initialising.
It also wires Spring Boot’s spring-boot-docker-compose support for the local profile, with
skip.in-tests: true so that support and ComposeContainer don’t both try to manage the same stack and fight over
ports. Prometheus and Grafana sit behind a compose profiles: key so integration tests don’t pay to start them,
while docker compose --profile observability up gives a developer the full local stack with the datasource
already provisioned.
The API mock for downstream clients
Whenever the service has REST, gRPC or GraphQL clients, the same compose stack gains an API mocking service, because an integration test that calls a real downstream service is not an integration test of this service: it is slow, it fails when someone else deploys, and it cannot reproduce the error responses the adapter is supposed to handle.
The mock is driven by the contracts already in apis/rest-client/<name>/, apis/grpc-client/<name>/ and
apis/graphql-client/<name>/ — the
same files each client is generated from. A parallel set of hand-written fixtures is never created, because two
descriptions of one contract diverge, and the day they do, the client compiles against one and passes its tests
against the other. Unlike Prometheus and Grafana, the mock sits behind no compose profile: tests need it, and so
does a developer running the service locally.
| Situation | Default | Why |
|---|---|---|
Any gRPC or GraphQL client, alone or alongside REST clients |
Microcks |
Mocks REST, gRPC and GraphQL from the same contracts in one container, consuming the OpenAPI spec, the |
REST clients only |
WireMock |
Far lighter and faster to start, with a finer-grained stub DSL for responses, delays, and faults. A
|
Both options are always offered, so a team already running Microcks for contract testing elsewhere keeps using it
even for a REST-only service. The answer is written back to the manifest as stack.apiMock so a re-run and every
later reader agree on one choice. With WireMock, the contract is wired in from both ends: stub mappings are
generated from each spec’s examples, and the OpenAPI validation extension makes a request or response that
violates the contract fail the test — which is what buys back most of the contract fidelity Microcks gets for
free.
Inputs
| Argument | Required | Description | Default |
|---|---|---|---|
|
No |
|
|
|
No |
Which API mocking tool backs the service’s REST/gRPC/GraphQL clients. Used without re-asking when the manifest
records it; otherwise chosen via |
Microcks with gRPC or GraphQL clients, WireMock for REST-only |
Client contracts |
No |
|
Read from the repository |
Image tags |
No |
Taken from the manifest if recorded there; otherwise the current stable release is looked up and written back into the manifest. |
Looked up, then persisted |
Docker |
No |
|
Verification skipped if unavailable |
Outputs
-
compose.yamlat the repository root — one pinned service per technology, each with a real readiness probe,depends_on: condition: service_healthywhere ordering genuinely matters, obviously-fake development-only credentials, named volumes only where local state is worth keeping, and bounded resource settings (notably Elasticsearch’s single-node mode and heap cap). -
observability/prometheus/prometheus.ymlandobservability/grafana/provisioning/datasources/prometheus.yaml, if metrics are enabled. -
The mock service, if the service has any client — with the client contracts mounted read-only, plus (for WireMock) generated stub mappings under
wiremock/mappings/<name>/, each carrying a header comment naming the contract and operation it came from so the next person edits the spec rather than the mapping, and a descriptor set underwiremock/grpc/when the gRPC extension is in play. -
stack.apiMockwritten back intospringboot-stack.yml. -
The integration-test base class(es) — a shared
ComposeContainer-based one for full-context tests, plus narrower per-adapter base classes that start only the one container that module needs, using@ServiceConnectionwhere a single container suffices and@DynamicPropertySourcefor the compose-based one. Each client’sclients.<name>.base-urlis bound to the mock’s randomly-assigned host port; a client-adapter*ITinstead uses the tool’s own Testcontainers module (MicrocksContainer/WireMockContainer), which starts in seconds against the same contract rather than bringing up the whole stack. -
Test-profile configuration per module, with the module’s own migration tooling left enabled so a broken migration fails a test rather than a deploy.
-
A report covering every service and its pinned tag, which containers each base class starts, whether migrations run in tests, and the result of each verification command.
Execution flow
Dependencies
Invokes
None. It reads springboot-stack.yml, writes the compose file and test harness, and updates the manifest’s image
tags and stack.apiMock.
It does depend on Setup Java Spring Boot APIs's output rather than invoking it: the client
contracts that skill writes under apis/rest-client/ and apis/grpc-client/ are what the mock serves, and the
examples it puts in them are what become the mocked responses.
Invoked by
-
Setup Java Spring Boot — Step 8, fourth of seven sub-skills, via the
iru-isolated-skill-executoragent. Runs after Setup Java Spring Boot APIs because the compose stack must know whether a schema registry is needed, and it completes the harness that the*ITtests written by Setup Java Spring Boot Modules depend on.
Also runs standalone (/iru-setup-java-springboot-testcontainers) to add a container to an existing service.
Related agents
-
iru-gate-runner— spawned for each of Step 6’s verification commands, since a fullmvn verifyagainst a container stack produces a very large log.
Source
SKILL.md on GitHub — the file this page was generated from.