Testing & Debugging

Debug locally in IntelliJ IDEA or Eclipse and validate workflows with representative HL7 inputs.

The SDK is designed for fast local feedback: run representative HL7 inputs under a debugger, inspect the parsed message and generated FHIR Bundle, then turn important scenarios into repeatable automated tests.

Local Bundle debugging

  1. Configure config/sdk.properties.
  2. Place a representative HL7 message under input/.
  3. Set breakpoints in shouldProcess, preConvert, postConvert, or reusable workflow classes.
  4. Run the SDK launcher in debug mode.
  5. Inspect the current MessageEnvelope, parsed HAPI HL7 message, and generated FHIR resources.
  6. Review the generated output/bundle.json.
What to inspect

Useful debugger values include messageCode, triggerEvent, messageControlId, the parsed HAPI HL7 object, and the generated FHIR Bundle. This makes it easy to compare the original HL7 input with the exact resources produced by the workflow.

IDE workflows

IDE

IntelliJ IDEA debugging

  1. Create or select the SDK launcher Run/Debug configuration.
  2. Set breakpoints in Main.groovy, lifecycle hooks, or workflow classes.
  3. Choose Debug and run a representative HL7 input.
  4. Inspect MessageEnvelope, HAPI HL7 objects, and the generated FHIR Bundle.
  5. Review output/bundle.json after execution.
IntelliJ IDEA debugging walkthrough

Debugging with FHIR server delivery

Local Bundle output is the safest default for development. When the integration must be exercised against a development FHIR server, switch the SDK output mode and configure the endpoint:

sdk.output.mode=fhirserver
fhir.server.url=http://localhost:8080/fhir

In this mode the SDK transforms the representative HL7 input and sends the generated FHIR Bundle to the configured development endpoint, allowing the complete script-and-delivery path to be debugged.

Use a development endpoint

Do not point local SDK debugging at a production FHIR server. Use an isolated development or test endpoint with representative test data.

Included test examples

The Developer SDK includes example Spock tests and reusable test helpers under src/test/groovy/. They provide a starting point for testing lifecycle hooks, semantic rules, HL7/FHIR scenarios, and project-specific regression cases.

src/test/groovy/
├─ MainSpec.groovy
├─ core/
│  ├─ EncounterRulesSpec.groovy
│  └─ PatientRulesSpec.groovy
└─ support/
   ├─ EncounterBuilder.groovy
   ├─ MessageEnvelopeBuilder.groovy
   ├─ PatientBuilder.groovy
   ├─ SemanticRuleTestKit.groovy
   └─ TestFixtures.groovy

The supplied tests demonstrate lifecycle-hook testing, semantic-rule testing, reusable fixtures, and helper builders for repeatable interoperability scenarios.

Run the test suite

From the SDK project root, run:

mvn clean test

This compiles the project and executes the included Spock/JUnit test suite. Add your own regression tests under src/test/groovy/ alongside the supplied examples.

Automated testing strategy

A practical SDK project normally uses three testing levels:

LevelBest forTypical approach
Unit testsHooks, helpers, normalizers, workflow classesCall Groovy methods directly with in-memory test fixtures.
Semantic-rule testsBusiness and interoperability validation rulesLoad the real rule and validate an in-memory FHIR Bundle.
SDK integration testsRepresentative HL7 → FHIR behaviorRun HL7 samples through the SDK and assert the generated FHIR output.

Unit testing workflow hooks with Spock

Lifecycle hooks are ordinary methods and can be tested directly without starting FireSonic or a FHIR server.

class MainSpec extends Specification {

    def "shouldProcess accepts message by default"() {
        given:
        def script = new Main()
        def envelope = envelope().build()

        when:
        FilterResult result = script.shouldProcess(envelope)

        then:
        result.isAccepted()
    }

    def "postConvert returns bundle unchanged"() {
        given:
        def script = new Main()
        def envelope = envelope().build()
        def bundle = bundleOf(
            patient().build(),
            encounter().build()
        )

        when:
        Bundle result = script.postConvert(envelope, bundle)

        then:
        result.is(bundle)
    }
}

Testing semantic rules

Semantic rules can be tested independently with an in-memory Bundle. The example below verifies the complete diagnostic payload, not only whether a failure occurred.

def "patient without identifier creates one identifier issue"() {
    given:
    Patient patient = patient()
        .setId("Patient/p1")
        .setName("Emma", "Miller")
        .withoutIdentifier()
        .build()

    when:
    def issues = rules.validate(
        "SEMANTIC-CORE-PAT-001",
        bundleOf(patient)
    )

    then:
    issues.size() == 1

    and:
    def issue = issues.first()
    issue.ruleId == "SEMANTIC-CORE-PAT-001"
    issue.severity.toString() == "ERROR"
    issue.message == "Patient does not contain an identifier"
    issue.resourceType == "Patient"
    issue.resourceId == "Patient/p1"
}

Data-driven Spock tests

Spock's where: tables are well suited to mapping and validation boundary cases where the same behavior must be checked against several input combinations.

@Unroll
def "patient name is invalid when #caseName"() {
    ...

    where:
    caseName                 | givenName | familyName
    "given name is missing"  | null      | "Miller"
    "given name is blank"    | "   "     | "Miller"
    "family name is missing" | "Emma"    | null
    "family name is blank"   | "Emma"    | "   "
    "both names are missing" | null      | null
}

Reusable test fixtures

Keep tests readable by centralizing repetitive FHIR and HL7 test-data construction in project test helpers. For example:

Patient patient = patient()
    .setId("Patient/p1")
    .withoutIdentifier()
    .build()

Encounter encounter = encounter()
    .setIdentifier("urn:test:encounter", "VN-1001")
    .build()

Bundle bundle = bundleOf(patient, encounter)

A project test fixture can also load representative HL7 files from src/test/resources:

MessageEnvelope envelope = envelope()
    .fromResource("hl7/adt-a01-with-multiple-al1.hl7")
    .build()
Keep examples representative

Store realistic HL7 samples under test resources and include edge cases that have occurred in real integrations. This turns production knowledge into repeatable regression coverage.

SDK integration tests

For end-to-end SDK tests, use representative HL7 files as input, run them through the local SDK processing path, and assert the generated FHIR Bundle or expected failure. This is the right level for verifying interaction between message normalization, conversion, script hooks, and semantic validation.

Keep external dependencies controlled: prefer local fixtures and mock/test endpoints unless the purpose of the test is specifically to verify integration with a development FHIR or REST service.