Best Practices - Smile CDR Documentation
Best Practices for Testing with SmileCdrContainer and SmileHarness
This document provides recommendations for effective testing with SmileCdrContainer and SmileHarness. Following these best practices will help you write more efficient, reliable, and maintainable tests.
Container Management
Reuse Containers When Possible Starting and stopping Docker containers is resource-intensive. When writing multiple tests that use the same container configuration, consider using a shared container instance:
@Testcontainers
class MyTestSuite {
// Shared container for all tests in this class
@Container
private static final SmileCdrContainer container = new SmileCdrContainer("2025.02.R02");
private static SmileHarness harness;
@BeforeAll
static void setup() {
harness = container.getHarness();
}
@Test
void test1() {
// Use the shared container and harness
}
@Test
void test2() {
// Use the same shared container and harness
}
}
Copy
Note that since the SmileCdrContainer is defined as static, it will be reused through the entire test class. Important to note that any data stored by one test will be accessible by another. This may not be what you want, so it is important to consider which lifecycle to use.
Use Appropriate Container Lifecycle
Consider the appropriate lifecycle for your container:
- Per Class: Use
@Containeron astaticfield to share a container across all tests in a class. - Per Test: Use
@Containeron a non-static field to create a fresh isolated container for each individual test.
Choose based on whether your tests need isolation or can share state. Set Appropriate Timeouts Docker containers can take time to start, especially for complex applications like Smile CDR. Set appropriate timeouts to avoid test failures due to slow startup:
SmileCdrContainer container = new SmileCdrContainer("2025.02.R02")
.withStartupTimeout(Duration.ofMinutes(5));
Copy
Test Design
Write Focused Tests Each test should focus on testing a specific functionality:
@Test
void testPatientCreation() {
// Test only patient creation
}
@Test
void testPatientSearch() {
// Test only patient search
}
Copy
Avoid writing tests that try to test too many things at once, as they become difficult to maintain and debug. Use Descriptive Test Names Use descriptive names for your tests that clearly indicate what is being tested:
@Test
void testCreatePatientWithMultipleIdentifiers() {
// Test creating a patient with multiple identifiers
}
Copy
Use Test Data Helpers Create helper methods or classes for generating test data to make your tests more readable and maintainable:
private Patient createTestPatient() {
Patient patient = new Patient();
patient.addName().setFamily("Test").addGiven("Patient");
patient.setGender(Enumerations.AdministrativeGender.MALE);
return patient;
}
Copy
Client Usage
Reuse Clients Create clients once and reuse them throughout your tests to avoid unnecessary overhead:
private static IGenericClient fhirClient;
private static AdminJsonRestClient adminClient;
@BeforeAll
static void setup() {
harness = container.getHarness();
fhirClient = harness.getSuperuserFhirClient();
adminClient = harness.getAdminJsonClient();
}
Copy
Use Appropriate Authentication Use the appropriate level of authentication for your tests:
- Use
getSuperuserFhirClient()for administrative tasks - Use
getFhirClient()with custom authentication for testing specific user scenarios
Configuration Management
Use Custom Properties Files For tests that require specific configurations, use custom properties files:
SmileCdrContainer container = new SmileCdrContainer()
.withPropertiesFile("custom-config.properties");
Copy
This allows you to tailor the Smile CDR configuration to your specific test needs.
Performance Considerations
Minimize Container Restarts Starting and stopping containers is expensive. Design your tests to minimize container restarts:
- Group related tests in the same class to share a container
- Use
@TestMethodOrderto control test execution order when necessary
Use Parallel Test Execution Carefully Testcontainers supports parallel test execution, but be careful with resource usage:
- Each container requires significant resources
- Too many containers running in parallel can slow down your tests or cause resource exhaustion
Consider Resource Requirements Smile CDR can be resource-intensive. Make sure your test environment has sufficient resources:
- Allocate enough memory to Docker
- Ensure enough CPU cores are available
- Monitor resource usage during test execution
Debugging
Enable Remote Debugging
For complex debugging scenarios where you want to add breakpoints to your own custom interceptor code, you can enable remote debugging on the container using withDebugEnabled():
SmileCdrContainer container = new SmileCdrContainer("2025.02.R02")
.withPropertiesFile("my-config.properties")
.withDebugEnabled();
Copy
This allows you to:
- Attach your IDE debugger to the running Smile CDR instance in the container
- Set breakpoints in your custom code and step through execution
- Inspect variables and evaluate expressions during runtime
The container will pause during startup until you attach a debugger (default behavior). If you want the container to start without waiting for the debugger, use:
SmileCdrContainer container = new SmileCdrContainer("2025.02.R02")
.withDebugEnabled(false); // Don't suspend until debugger attaches
Copy
See the Enabling Remote Debugging section in the Usage guide for detailed instructions on connecting your debugger.
Inspect Container State Use the Testcontainers API to inspect the container state:
// Get container logs
String logs = container.getLogs();
// Execute a command in the container
ExecResult result = container.execInContainer("ls", "-la");
Copy
Advanced Techniques
Custom Wait Strategies By default, SmileCdrContainer is considered started when the "You are up and running" log message arrives in the logs. However, you can use custom wait strategies to ensure the container is fully ready before tests, if you have some alternate start conditions.
SmileCdrContainer container = new SmileCdrContainer("2025.02.R02")
.waitingFor(Wait.forHttp("/Patient")
.forPort(8000)
.withBasicCredentials("admin", "password")
.forStatusCode(200));
Copy
Conclusion
By following these best practices, you can write more effective tests with SmileCdrContainer and SmileHarness. Remember that good tests are:
- Focused: Each test should test one thing
- Fast: Tests should run quickly to provide rapid feedback
- Reliable: Tests should produce consistent results
- Maintainable: Tests should be easy to understand and modify
- Isolated: Tests should not depend on or affect other tests
Best Practices
Testing Tools