# 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:

```java
@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 `@Container` on a `static` field to share a container across all tests in a class.
- **Per Test**: Use `@Container` on 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:

```java
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:

```java
@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:

```java
@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:

```java
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:

```java
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:

```java
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 `@TestMethodOrder` to 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()`:

```java
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:

```java
SmileCdrContainer container = new SmileCdrContainer("2025.02.R02")
    .withDebugEnabled(false);  // Don't suspend until debugger attaches
```

Copy

See the  [Enabling Remote Debugging](/content/docs/test_tools/usage.html#enabling-remote-debugging/index.html) section in the Usage guide for detailed instructions on connecting your debugger.

**Inspect Container State**
Use the Testcontainers API to inspect the container state:

```java
// 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.

```java
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

[Clients](/content/docs/test_tools/clients.html)

Best Practices

Testing Tools

[Overview](/content/docs/test_tools/overview.html)
[Usage](/content/docs/test_tools/usage.html)
[Clients](/content/docs/test_tools/clients.html)
[Best Practices](/content/docs/test_tools/bestpractices.html)
