Testcontainers Setup¶
Run FHIR tests against an automatically managed HAPI FHIR container. No manual server setup required.
Prerequisites¶
- Docker installed and running
fhir-frog-test-fixtureson the test classpath
Add Testcontainers Dependency¶
pom.xml
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>junit-jupiter</artifactId>
<scope>test</scope>
</dependency>
Standard HAPI Server¶
For tests against a plain FHIR R4 server with no IG:
@Testcontainers
@ExtendWith(FhirFrogExtension.class)
class PatientIT {
@Container
static GenericContainer<?> hapi = new GenericContainer<>("hapiproject/hapi:v7.6.0")
.withExposedPorts(8080)
.waitingFor(Wait.forHttp("/fhir/metadata").withStartupTimeout(Duration.ofMinutes(3)));
@FhirTestConfig
FhirTestConfig config() {
return FhirTestConfig.builder()
.serverUrl("http://localhost:" + hapi.getMappedPort(8080) + "/fhir")
.fixtureBasePath(Path.of("src/test/resources/fhir"))
.build();
}
@TestTemplate
@TestScriptSource("fhir/patient-lifecycle.tsh")
void patientLifecycle(FhirTestConfig config) { }
}
Pre-warmed IG Server (Bootstrap Pattern)¶
For IGs with large dependency trees (e.g. AU Core, AU eRequesting), a cold HAPI startup can take 10–20 minutes. Use the bootstrap pattern to create a pre-warmed Docker image once and reuse it:
Then in tests:
@Testcontainers
class AUCoreIT {
@Container
static GenericContainer<?> hapi = new GenericContainer<>("hapi-au-core:1.0.0")
.withExposedPorts(8080)
.waitingFor(Wait.forHttp("/fhir/metadata").withStartupTimeout(Duration.ofSeconds(60)));
// ~25 seconds startup vs ~15 minutes cold
}
See Ant Task for the full bootstrap workflow.
Shared Container (Class-Level)¶
For performance, share one container across all tests in a class:
@Testcontainers
class PatientSuiteIT {
@Container
static final GenericContainer<?> HAPI =
new GenericContainer<>("hapiproject/hapi:v7.6.0")
.withExposedPorts(8080)
.waitingFor(Wait.forHttp("/fhir/metadata")
.withStartupTimeout(Duration.ofMinutes(3)));
private FhirTestConfig config() {
return FhirTestConfig.builder()
.serverUrl("http://localhost:" + HAPI.getMappedPort(8080) + "/fhir")
.build();
}
}
static + @Container means the container starts once for the whole test class.
Tips¶
- Use
waitingFor(Wait.forHttp("/fhir/metadata"))— HAPI is ready when metadata responds - Increase
withStartupTimeoutfor IGs with many packages - Use
@Container staticto avoid starting/stopping between tests - See bootstrap pattern for IG-heavy setups
Next Steps¶
- JUnit Deployment — Full JUnit extension reference
- Ant Task — Bootstrap pre-warmed images
- AU Core Example — Real-world IG example