This tutorial will guide you through the process of writing tests that use SmileCdrContainer
and SmileHarness
to interact with a Smile CDR instance. We will walk through existing sample tests, explaining the behaviour.
Before you begin, make sure you have:
First, download the cdr-interceptor-starterproject zip file or tarball. This will provide you with a baseline test class that we will explore for the rest of this tutorial.
In your terminal, navigate to the project root, and run the following command to build the project locally.
mvn clean install -DskipTests
Once that is done, you are free to run the following command, which will run all the tests in the project.
mvn verify
Note that this may take some time. Once it is finished, you should see that maven reports that all tests are passing.
To start, open BoilerplateIT.java
, located in cdr-interceptor-starterproject/src/test/java/com/smilecdr/demo/examples/
, in your preferred IDE. Let's walk through this sample test class, and explain what each part does, so you can start building your own tests.
package com.smilecdr.demo.examples;
import ca.cdr.test.extensions.SmileCdrContainer;
import ca.cdr.test.app.harness.api.SmileHarness;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.FhirVersionEnum;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;
import org.testcontainers.utility.DockerImageName;
import static org.assertj.core.api.Assertions.assertThat;
@Testcontainers
class BoilerplateIT {
@Container
private static SmileCdrContainer container = new SmileCdrContainer();
private static SmileHarness harness;
@BeforeAll
public static void setup() {
harness = container.getHarness();
}
@Test
void testSmileCdrIsRunning() {
// This test will pass if the container starts successfully
assertThat(container.isRunning()).isTrue();
//Check that we can determine the FhirContext from the running server.
assertThat(harness.getFhirContext().getVersion().getVersion().toString()).isEqualTo("R4");
}
}
This test simply verifies that the Smile CDR container starts successfully, and that we can connect to it through the harness. When you run this test, it will:
Here's a breakdown of some of the setup you see in this class:
@Testcontainers
– This tells the testing framework to control the lifecycle of docker containers in this test.@Container private SmileCdrContainer ourContainer;
– This tells Testcontainers that we will be creating a SmileCdrContainer
to be managed for the duration of this test class.harness = conatiner.getHarness()
– After it starts, we ask the SmileCdrContainer to give us a test harness, so that we can write tests which connect to the running Smile CDR container.This test on its own is not very useful, so the remainder of this tutorial will be a walk through the existing test classes that live alongside BoilerplateIT.java
This file contains multiple tests related to interacting with the FHIR API on the running Smile CDR instance.
Starting out easy, the following test simply creates a Patient object, retrieves it, and validates that it has the name it was just given.
@Test
void testCreateAndRetrievePatientAsAdmin() {
// Get a FHIR client with admin credentials
IGenericClient fhirClient = harness.getSuperuserFhirClient();
// Create a new patient
Patient patient = new Patient();
patient.addName().setFamily("Smith").addGiven("John");
patient.setGender(Enumerations.AdministrativeGender.MALE);
// Save the patient to the server
patient = (Patient) fhirClient.create().resource(patient).execute().getResource();
// Verify the patient was created with an ID
assertThat(patient.getIdElement().getValue()).isNotNull();
String patientId = patient.getIdElement().getIdPart();
// Retrieve the patient by ID
Patient retrievedPatient = fhirClient.read().resource(Patient.class).withId(patientId).execute();
// Verify the retrieved patient matches what we created
assertThat(retrievedPatient.getName().get(0).getFamily()).isEqualTo("Smith");
assertThat(retrievedPatient.getName().get(0).getGiven().get(0).toString()).isEqualTo("John");
}
See that we ask our harness to provide us with a FHIR-compatible client by calling harness.getSuperuserFhirClient()
. This method returns an IGenericClient which is capable of communicating with Smile CDR. If you are unfamiliar with this class, please read the IGenericClient documentation, as it will be used in all the tests in this class.
The harness allows you to fetch an IGenericClient in multiple ways. All of these are documented in the API Docs for SmileHarness;
The following test demonstrates how to create a patient and observations, and then use a transaction bundle to search for these resources:
@Test
void testTransactionBundleWithPatientAndObservations() {
// Get a FHIR client with admin credentials
IGenericClient fhirClient = harness.getSuperuserFhirClient();
// Create a new patient
Patient patient = new Patient();
patient.addIdentifier().setSystem("http://example.org/mrn").setValue("12345");
patient.addName().setFamily("Johnson").addGiven("Robert");
patient.setGender(Enumerations.AdministrativeGender.MALE);
// Save the patient to the server
MethodOutcome patientOutcome = fhirClient.create().resource(patient).execute();
assertThat(patientOutcome).isNotNull();
assertThat(patientOutcome.getId()).isNotNull();
String patientId = patientOutcome.getId().getIdPart();
// Create two observations linked to the patient
Observation observation1 = new Observation();
observation1.setStatus(Observation.ObservationStatus.FINAL);
observation1.getSubject().setReference("Patient/" + patientId);
observation1.getCode().addCoding()
.setSystem("http://loinc.org")
.setCode("8867-4")
.setDisplay("Heart rate");
observation1.setValue(new Quantity().setValue(72).setUnit("beats/minute"));
// Save the first observation
MethodOutcome obs1Outcome = fhirClient.create().resource(observation1).execute();
assertThat(obs1Outcome).isNotNull();
assertThat(obs1Outcome.getId()).isNotNull();
// Create and save the second observation
Observation observation2 = new Observation();
observation2.setStatus(Observation.ObservationStatus.FINAL);
observation2.getSubject().setReference("Patient/" + patientId);
observation2.getCode().addCoding()
.setSystem("http://loinc.org")
.setCode("8480-6")
.setDisplay("Systolic blood pressure");
observation2.setValue(new Quantity().setValue(120).setUnit("mm[Hg]"));
MethodOutcome obs2Outcome = fhirClient.create().resource(observation2).execute();
assertThat(obs2Outcome).isNotNull();
assertThat(obs2Outcome.getId()).isNotNull();
// Now demonstrate a transaction bundle that searches for these resources
Bundle searchBundle = new Bundle();
searchBundle.setType(Bundle.BundleType.TRANSACTION);
// Add a search for the patient
searchBundle.addEntry()
.getRequest()
.setMethod(HTTPVerb.GET)
.setUrl("Patient?identifier=http://example.org/mrn|12345");
// Add a search for observations related to this patient
searchBundle.addEntry()
.getRequest()
.setMethod(HTTPVerb.GET)
.setUrl("Observation?subject=Patient/" + patientId);
// Execute the transaction
Bundle responseBundle = fhirClient.transaction().withBundle(searchBundle).execute();
// Verify the response
assertThat(responseBundle).isNotNull();
assertThat(responseBundle.getType()).isEqualTo(Bundle.BundleType.TRANSACTIONRESPONSE);
assertThat(responseBundle.getEntry().size()).isEqualTo(2);
// The first entry should contain the patient search results
Bundle patientSearchResult = (Bundle) responseBundle.getEntry().get(0).getResource();
assertThat(patientSearchResult).isNotNull();
assertThat(patientSearchResult.getEntry().size()).isEqualTo(1);
// The second entry should contain the observation search results
Bundle obsSearchResult = (Bundle) responseBundle.getEntry().get(1).getResource();
assertThat(obsSearchResult).isNotNull();
assertThat(obsSearchResult.getEntry().size()).isEqualTo(2);
}
This test:
The following test demonstrates conditional create functionality, which allows you to create a resource only if it doesn't already exist:
@Test
void testConditionalCreateBundle() {
// Get a FHIR client with admin credentials
IGenericClient fhirClient = harness.getSuperuserFhirClient();
// Create a unique identifier for this test
String patientIdentifierSystem = "http://example.org/mrn";
String patientIdentifierValue = "67890-" + System.currentTimeMillis(); // Make it unique
// Create a patient directly (not using conditional create)
Patient patient = new Patient();
patient.addIdentifier().setSystem(patientIdentifierSystem).setValue(patientIdentifierValue);
patient.addName().setFamily("Davis").addGiven("Sarah");
patient.setGender(Enumerations.AdministrativeGender.FEMALE);
// Create the patient directly
MethodOutcome outcome = fhirClient.create().resource(patient).execute();
// Verify the outcome
assertThat(outcome.getCreated()).isTrue();
assertThat(outcome.getId()).isNotNull();
String firstPatientId = outcome.getId().getIdPart();
// Create a second patient with the same identifier but different name
Patient patient2 = new Patient();
patient2.addIdentifier().setSystem(patientIdentifierSystem).setValue(patientIdentifierValue);
patient2.addName().setFamily("Davis").addGiven("Jane"); // Different given name
patient2.setGender(Enumerations.AdministrativeGender.FEMALE);
// Try to create it using conditional create
MethodOutcome outcome2 = fhirClient.create()
.resource(patient2)
.conditional()
.where(Patient.IDENTIFIER.exactly().systemAndCode(patientIdentifierSystem, patientIdentifierValue))
.execute();
// Verify the outcome - should return the same ID as the first patient, and the outcome should indicate
// that there was no creation
assertThat(outcome2.getId()).isNotNull();
String secondPatientId = outcome2.getId().getIdPart();
// The IDs should be the same since the conditional create should find the existing patient
assertThat(secondPatientId).isEqualTo(firstPatientId);
// Read the original patient directly to verify it still has the original name
Patient retrievedPatient = fhirClient.read().resource(Patient.class).withId(firstPatientId).execute();
assertThat(retrievedPatient).isNotNull();
assertThat(retrievedPatient.getName().get(0).getGiven().get(0).toString()).isEqualTo("Sarah");
}
This test:
The following test demonstrates how to create and delete a FHIR resource:
@Test
void testCreateAndDeleteObservation() {
// Get a FHIR client with admin credentials
IGenericClient fhirClient = harness.getSuperuserFhirClient();
// Create a new observation
Observation observation = new Observation();
observation.setStatus(Observation.ObservationStatus.FINAL);
observation.getCode().addCoding()
.setSystem("http://loinc.org")
.setCode("8310-5")
.setDisplay("Body temperature");
observation.setValue(new Quantity().setValue(37.0).setUnit("C"));
// Save the observation to the server
MethodOutcome outcome = fhirClient.create().resource(observation).execute();
// Verify the observation was created with an ID
assertThat(outcome.getId()).isNotNull();
String observationId = outcome.getId().getIdPart();
// Retrieve the observation by ID to confirm it exists
Observation retrievedObservation = fhirClient.read().resource(Observation.class).withId(observationId).execute();
assertThat(retrievedObservation).isNotNull();
// Delete the observation
fhirClient.delete().resourceById("Observation", observationId).execute();
// Try to retrieve the observation again - should throw ResourceGoneException
try {
fhirClient.read().resource(Observation.class).withId(observationId).execute();
// If we get here, the test should fail
fail("Expected ResourceGoneException was not thrown");
} catch (Exception e) {
// Expected exception - observation should be gone
assertThat(e.getMessage()).contains("HTTP 410 Gone");
}
}
This test:
The following test demonstrates how to use the $validate operation to validate a FHIR resource:
@Test
void testValidateOperation() {
// Get a FHIR client with admin credentials
IGenericClient fhirClient = harness.getSuperuserFhirClient();
// Create a patient
Patient patient = new Patient();
patient.addIdentifier().setSystem("http://example.org/mrn").setValue("54321");
patient.addName().setFamily("Wilson").addGiven("James");
patient.setGender(Enumerations.AdministrativeGender.MALE);
// Save the patient to the server
MethodOutcome createOutcome = fhirClient.create().resource(patient).execute();
String patientId = createOutcome.getId().getIdPart();
// Retrieve the patient
// Validate the patient using the $validate operation
Parameters outParams = fhirClient.operation()
.onInstance(new IdType("Patient", patientId))
.named("$validate")
.withNoParameters(Parameters.class)
.execute();
// Extract the validation result
OperationOutcome outcome = (OperationOutcome) outParams.getParameter().get(0).getResource();
// Verify the validation was successful
assertThat(outcome).isNotNull();
assertThat(outcome.getIssue()).isNotEmpty();
// Check if there are any errors
boolean hasErrors = outcome.getIssue().stream()
.anyMatch(issue -> issue.getSeverity() == OperationOutcome.IssueSeverity.ERROR);
assertThat(hasErrors).isFalse();
//Given that we haven't added a narrative, the default validation should warn about that!
List<OperationOutcome.OperationOutcomeIssueComponent> warnings = outcome.getIssue().stream()
.filter(issue -> issue.getSeverity() == OperationOutcome.IssueSeverity.WARNING)
.collect(Collectors.toUnmodifiableList());
assertThat(warnings).hasSize(1);
assertThat(warnings.get(0).getDetails().getCodingFirstRep().getCode()).contains("dom-6");
}
This test:
The following test demonstrates how to use the _include parameter to include referenced resources in search results:
@Test
void testSearchWithInclude() {
// Get a FHIR client with admin credentials
IGenericClient fhirClient = harness.getSuperuserFhirClient();
// Create a patient
Patient patient = new Patient();
patient.addIdentifier().setSystem("http://example.org/mrn").setValue("98765");
patient.addName().setFamily("Brown").addGiven("Michael");
MethodOutcome patientOutcome = fhirClient.create().resource(patient).execute();
String patientId = patientOutcome.getId().getIdPart();
// Create an observation linked to the patient
Observation observation = new Observation();
observation.setStatus(Observation.ObservationStatus.FINAL);
observation.getSubject().setReference("Patient/" + patientId);
observation.getCode().addCoding()
.setSystem("http://loinc.org")
.setCode("8302-2")
.setDisplay("Body height");
observation.setValue(new Quantity().setValue(180).setUnit("cm"));
fhirClient.create().resource(observation).execute();
// Search for observations and include the referenced patient
Bundle searchResult = fhirClient.search()
.forResource(Observation.class)
.where(Observation.SUBJECT.hasId(patientId))
.include(Observation.INCLUDE_PATIENT)
.returnBundle(Bundle.class)
.execute();
// Verify we got both observation and patient resources
assertThat(searchResult.getEntry().size()).isEqualTo(2);
// Check that we have at least one Patient resource in the results
boolean hasPatient = searchResult.getEntry().stream()
.anyMatch(entry -> entry.getResource() instanceof Patient);
assertThat(hasPatient).isTrue();
// Check that we have at least one Observation resource in the results
boolean hasObservation = searchResult.getEntry().stream()
.anyMatch(entry -> entry.getResource() instanceof Observation);
assertThat(hasObservation).isTrue();
}
This test:
The following test demonstrates how to use a transaction bundle to create multiple resources in a single request:
@Test
void testTransactionBundleWithMultiplePatients() {
// Get a FHIR client with admin credentials
IGenericClient fhirClient = harness.getSuperuserFhirClient();
// Create a transaction bundle
Bundle transactionBundle = new Bundle();
transactionBundle.setType(Bundle.BundleType.TRANSACTION);
// Add 5 random patients to the bundle
for (int i = 0; i < 5; i++) {
// Create a new patient with random data
Patient patient = new Patient();
// Add a unique identifier
String uniqueId = "patient-" + System.currentTimeMillis() + "-" + i;
patient.addIdentifier().setSystem("http://example.org/patients").setValue(uniqueId);
// Add the patient to the transaction bundle
transactionBundle.addEntry()
.setResource(patient)
.getRequest()
.setMethod(HTTPVerb.POST)
.setUrl("Patient");
}
// Execute the transaction
Bundle responseBundle = fhirClient.transaction().withBundle(transactionBundle).execute();
// Verify the response
assertThat(responseBundle).isNotNull();
assertThat(responseBundle.getType()).isEqualTo(Bundle.BundleType.TRANSACTIONRESPONSE);
assertThat(responseBundle.getEntry().size()).isEqualTo(5);
// Verify we can search for the created patients
Bundle searchBundle = fhirClient.search()
.forResource(Patient.class)
.where(Patient.IDENTIFIER.hasSystemWithAnyCode("http://example.org/patients"))
.returnBundle(Bundle.class)
.execute();
// We should find exactly 5 patients with our system
assertThat(searchBundle.getEntry().size()).isEqualTo(5);
}
This test:
Now, we move on from FHIR interactions, and start looking at some other functionality provided by SmileHarness, the AdminJsonRestClient. This client is built to interact with modules of type ADMIN_JSON
. It has a multitude of convenience methods documented in the Javadocs.
@Test
void testGetNodeConfigurations() {
// Get an Admin JSON client
AdminJsonRestClient adminClient = harness.getAdminJsonClient();
// Get the node configurations
NodeConfigurations config = adminClient.getNodeConfigurations();
// Verify we have at least one node
assertThat(config.getNodes()).isNotEmpty();
// Get the first node
NodeConfigurations.NodeConfiguration node = config.getNodes().get(0);
// Verify the node has the expected ID
assertThat(node.getNodeId()).isEqualTo("Master");
// Verify the node has modules
assertThat(node.getModules()).isNotEmpty();
// Find the FHIR endpoint module
Optional<NodeConfigurations.ModuleConfiguration> fhirModule = node.getModule("fhir_endpoint");
assertThat(fhirModule).isPresent();
// Verify the FHIR endpoint port
String portValue = fhirModule.get().getConfigProperty("port");
assertThat(portValue).isEqualTo("8000");
}
This test:
@Test
public void testCreateUserWithStrongPasswordSucceeds() {
// Get an Admin JSON client
AdminJsonRestClient adminClient = harness.getAdminJsonClient();
UserDetailsJson userDetailsJson = new UserDetailsJson();
userDetailsJson.setUsername("a-test-user");
userDetailsJson.setPassword("HJ!cJm9qO!tYzy");
userDetailsJson.setEmail("an-email@address.com");
//Ensure the user has no ID
assertThat(userDetailsJson.getPid()).isNull();
userDetailsJson = adminClient.userCreate("Master", "local_security", userDetailsJson);
//Ensure the user has an ID now
assertThat(userDetailsJson.getPid()).isNotNull();
}
This test:
@Test
public void testCreateUserWithWeakPasswordFails() {
// Get an Admin JSON client
AdminJsonRestClient adminClient = harness.getAdminJsonClient();
UserDetailsJson userDetailsJson = new UserDetailsJson();
userDetailsJson.setUsername("test-user");
userDetailsJson.setPassword("weakpassword");
userDetailsJson.setEmail("email@address.com");
try {
adminClient.userCreate("Master", "local_security_in", userDetailsJson);
fail();
} catch (Exception e) {
assertThat(e).hasMessageContaining("New password should have at least 3 character types including lowercase letters, uppercase letters, numbers, and symbols.");
}
}
This test:
@Test
public void testCreateUserWithSpecificAuthorities() {
//Build a set of authorities that will allow a user to hit /metadata, abd /Observation
GrantedAuthorityJson readAllObservationsPermission = new GrantedAuthorityJson(PermissionEnum.FHIR_READ_ALL_OF_TYPE, "Observation?");
GrantedAuthorityJson metadataPermission = new GrantedAuthorityJson(PermissionEnum.FHIR_CAPABILITIES);
GrantedAuthorityJson fhirClient = new GrantedAuthorityJson(PermissionEnum.ROLE_FHIR_CLIENT);
// Get an Admin JSON client
AdminJsonRestClient adminClient = harness.getAdminJsonClient();
UserDetailsJson userDetailsJson = new UserDetailsJson();
userDetailsJson.setUsername("observation-user");
userDetailsJson.setPassword("HJ!cJm9qO!tYzy");
userDetailsJson.setEmail("an-email@address.com");
userDetailsJson.addAuthorities(fhirClient, metadataPermission, readAllObservationsPermission);
//Ensure the user has no ID
assertThat(userDetailsJson.getPid()).isNull();
userDetailsJson = adminClient.userCreate("Master", "local_security", userDetailsJson);
//Ensure the user has an ID now
assertThat(userDetailsJson.getPid()).isNotNull();
IGenericClient userFhirClient = harness.getFhirClient();
userFhirClient.registerInterceptor(new BasicAuthInterceptor("observation-user", "HJ!cJm9qO!tYzy"));
//Ensure they can query observations
Bundle observations = userFhirClient.search().forResource("Observation").returnBundle(Bundle.class).execute();
assertThat(observations.getEntry()).hasSize(0);
//Ensure they can't query another arbitrary resource type.
try {
userFhirClient.search().forResource("DiagnosticReport").returnBundle(Bundle.class).execute();
} catch (ForbiddenOperationException e) {
assertThat(e).hasMessageContaining("HTTP 403 Forbidden: Access denied");
}
}
This test:
Smile CDR supports HL7v2 messaging, and the SmileHarness
provides an HL7V2RestClient
that you can use to send HL7v2 messages in your tests. This section demonstrates how to use the HL7v2 client in your integration tests.
The first step is to get an HL7v2 client from the harness:
@Test
void testGetHL7V2Client() {
// Get an HL7V2 client
HL7V2RestClient hl7v2Client = harness.getHL7V2RestClient();
// Verify the client was created successfully
assertThat(hl7v2Client).isNotNull();
}
This test:
One common HL7v2 message type is the ADT (Admission, Discharge, Transfer) message. Here's how to create and send an ADT_A01 message:
@Test
void testSendADTMessage() throws HL7Exception {
// Get an HL7V2 client
HL7V2RestClient hl7v2Client = harness.getHL7V2RestClient();
// Create a sample ADT_A01 (admission) message
ADT_A01 adt = new ADT_A01();
MSH msh = adt.getMSH();
// Set required MSH fields
msh.getFieldSeparator().setValue("|");
msh.getEncodingCharacters().setValue("^~\\&");
msh.getSendingApplication().getNamespaceID().setValue("TestApp");
msh.getSendingFacility().getNamespaceID().setValue("TestFacility");
msh.getReceivingApplication().getNamespaceID().setValue("SmileCDR");
msh.getReceivingFacility().getNamespaceID().setValue("SmileCDR");
msh.getDateTimeOfMessage().getTime().setValue("20230101120000");
msh.getMessageType().getMessageCode().setValue("ADT");
msh.getMessageType().getTriggerEvent().setValue("A01");
msh.getMessageType().getMessageStructure().setValue("ADT_A01");
msh.getMessageControlID().setValue("123456");
msh.getProcessingID().getProcessingID().setValue("P");
msh.getVersionID().getVersionID().setValue("2.5");
// Set patient information
adt.getPID().getPatientID().getIDNumber().setValue("12345");
adt.getPID().getPatientName(0).getFamilyName().getSurname().setValue("Smith");
adt.getPID().getPatientName(0).getGivenName().setValue("John");
adt.getPID().getDateTimeOfBirth().getTime().setValue("19800101");
adt.getPID().getAdministrativeSex().setValue("M");
// Send the message to the server
Message response = hl7v2Client.sendMessage(adt);
// Verify we received a response
assertThat(response).isNotNull();
// Verify the response is an acknowledgment
assertThat(response.getName()).contains("ACK");
}
This test:
You can also send HL7v2 messages as raw strings:
@Test
void testSendStringMessage() throws HL7Exception {
// Get an HL7V2 client
HL7V2RestClient hl7v2Client = harness.getHL7V2RestClient();
// Create a raw HL7v2 message as a string
String rawMessage = "MSH|^~\\&|TestApp|TestFacility|SmileCDR|SmileCDR|20230101120000||ADT^A01^ADT_A01|123456|P|2.5\r" +
"PID|||12345||Smith^John||19800101|M";
// Send the message to the server
Message response = hl7v2Client.sendMessage(rawMessage);
// Verify we received a response
assertThat(response).isNotNull();
// Verify the response is an acknowledgment
assertThat(response.getName()).contains("ACK");
}
This test:
Smile CDR provides a test data helper class that can generate sample HL7v2 messages for you:
@Test
void testUsingTestDataHelper() throws HL7Exception {
// Get an HL7V2 client
HL7V2RestClient hl7v2Client = harness.getHL7V2RestClient();
// Get the FHIR context from the harness
FhirContext fhirContext = harness.getFhirContext();
// Create a test data helper
Hl7V2TestDataHelper testDataHelper = Hl7V2TestDataHelper.buildDefault(fhirContext);
// Create a sample ADT_A01 message using the helper
ADT_A01 adtA01 = testDataHelper.createAdtA01();
// Send the message to the server
Message response = hl7v2Client.sendMessage(adtA01);
// Verify we received a response
assertThat(response).isNotNull();
// Verify the response is an acknowledgment
assertThat(response.getName()).contains("ACK");
}
This test:
Everything we've seen so far has been running smile CDR in its Out-of-box-experience mode. This means that:
To support writing automated tests against a specific configuration, SmileCdrContainer allows you to customize all of the above to suit your own needs.
The node configuration properties file controls the behaviour of Smile CDR. When booting a SmileCdrContainer, it will use the default cdr-config-master.properties
that is shipped with Smile CDR. You may customize that by calling withPropertiesFile()
on the container instance.
withPropertiesFile(String thePropertiesFilename)
&ndash: If you invoke this method on the SmileCdrContainer during setup, it will use the file you have provided instead of the default properties file. This file must be on the classpath.This allows you to customize Smile CDR to your specific test case. You can:
Let's have a look at a simple example, CustomConfigurationIT.java
.
@Container
private static final SmileCdrContainer container = new SmileCdrContainer()
.withPropertiesFile("config-with-two-persistence.properties");
private static SmileHarness harness;
@BeforeAll
public static void setup() {
harness = container.getHarness();
}
This test demonstrates how to use a custom properties file with SmileCdrContainer. The key part is using the withPropertiesFile()
method to specify your custom configuration file. Have a look at the config-with-two-persistence.properties
to see what this looks like.
This new custom configuration file does two specific things:
persistence_two
, which is also an R4 Persistence module.fhir_endpoint_two
which is hosted on port 8888
@Test
void testCreatePatientsOnTwoSeparateRepositories() {
// Get a FHIR client with admin credentials for fhir_endpoint
IGenericClient fhirClientFirstModule = harness.getSuperuserFhirClient(8000);
// Get a FHIR client with admin credentials for fhir_endpoint_two
IGenericClient fhirClientSecondModule = harness.getSuperuserFhirClient(8888);
// Create a new patient
Patient patient = new Patient();
patient.addName().setFamily("Johnson").addGiven("Robert");
patient.setGender(Enumerations.AdministrativeGender.MALE);
// Save the patient to the first endpoint
patient = (Patient) fhirClientFirstModule.create().resource(patient).execute().getResource();
// Retrieve the patient by ID
Patient retrievedPatient = fhirClientFirstModule.read().resource(Patient.class).withId(patient.getId()).execute();
// Verify the retrieved patient matches what we created
assertThat(retrievedPatient.getName().get(0).getFamily()).isEqualTo("Johnson");
assertThat(retrievedPatient.getName().get(0).getGiven().get(0).toString()).isEqualTo("Robert");
//Ensure that the second client doesn't know about it(since its in a different repository!)
try {
fhirClientSecondModule.read().resource(Patient.class).withId(patient.getId()).execute();
} catch (ResourceNotFoundException e) {
assertThat(e).hasMessageContaining("HTTP 404 Not Found");
}
}
You must take care when providing a custom properties file. SmileCdrContainer and SmileHarness expect the default credentials to work, and also expect the JSON Admin API to be running on its default port (9000).
If you modify your configuration in such a way that any of the above are no longer true, you'll notice that the Smile Harness cannot connect to the running container. This can be remedied by providing a custom HarnessContext
object.
Have a look at config-with-alternate-admin-json.properties
in your project's resources directory. This file should contain the custom configuration for your Smile CDR instance. The specific change we care about here is that we are modifying the Admin JSON module's port value:
Default value:
module.admin_json.config.port =9000
Modified value:
module.admin_json.config.port =9123
In this example, we have modified our configuration properties file to have the Admin Json module run on port 9123, so we will have to provide a custom HarnessContext
to the container.
// In this example, we're using a custom properties file that configures
// the Admin JSON module to run on port 9123 instead of the default 9000
@Container
private static SmileCdrContainer container = new SmileCdrContainer()
.withPropertiesFile("config-with-alternate-admin-json.properties")
.withCustomHarnessContext(new HarnessContext(
"http",
"localhost", // baseUrl
9123, // jsonAdminPort - matches our custom config
"admin", // username
"password" // password
));
private static SmileHarness harness;
@BeforeAll
public static void setup() {
harness = container.getHarness();
}
This test demonstrates how to use a custom HarnessContext with SmileCdrContainer. The key part is using the withCustomHarnessContext()
method to specify a custom HarnessContext that matches your modified configuration:
@Test
void testGetAdminControllerWithCustomHarnessContext() {
// Get a FHIR client with admin credentials
AdminJsonRestClient adminJsonClient = harness.getAdminJsonClient(9123);
// Ensure we got the client
assertThat(adminJsonClient).isNotNull();
// Ensure that the bootstrapping worked, and we can retrieve other clients
IGenericClient fhirClient = harness.getSuperuserFhirClient();
IFetchConformanceUntyped capabilities = fhirClient.capabilities();
assertThat(capabilities).isNotNull();
}
In this tutorial, you've learned how to:
SmileCdrContainer
and SmileHarness
These examples should give you a good starting point for writing your own tests. Remember that SmileCdrContainer
and SmileHarness
are designed to make testing with Smile CDR easier, so take advantage of their features to write comprehensive tests for your applications.
For more information on the clients available through SmileHarness
, see Available Clients.
For recommendations on effective testing with SmileCdrContainer
and SmileHarness
, see Best Practices.