Skip to content

Negative Testing

Test that your FHIR server correctly rejects invalid requests and returns appropriate error responses.

Why Negative Tests?

Conformance isn't just about accepting valid data — it's also about rejecting invalid data correctly. Negative tests verify:

  • Missing required fields → 400 Bad Request
  • Unknown resources → 404 Not Found
  • Validation failures → 422 Unprocessable Entity
  • Unauthorized access → 401 / 403

Test 404 for Missing Resource

{
  "test": [
    {
      "name": "Missing resource returns 404",
      "action": [
        {
          "operation": {
            "type": { "code": "read" },
            "resource": "Patient",
            "params": "/this-id-does-not-exist",
            "responseId": "missingResponse"
          }
        },
        {
          "assert": {
            "description": "Returns 404",
            "responseCode": "404",
            "warningOnly": false
          }
        }
      ]
    }
  ]
}

Test Validation Failure (422)

Submit a resource that violates a profile constraint:

fixtures/invalid-patient.json
{
  "resourceType": "Patient",
  "identifier": [
    {
      "system": "http://ns.electronichealth.net.au/id/medicare-number",
      "value": "not-a-valid-medicare-number"
    }
  ]
}
{
  "test": [
    {
      "name": "Invalid medicare number rejected",
      "action": [
        {
          "operation": {
            "type": { "code": "create" },
            "resource": "Patient",
            "sourceId": "invalid-patient",
            "responseId": "createResponse"
          }
        },
        {
          "assert": {
            "description": "Server rejects invalid identifier",
            "responseCode": "422",
            "warningOnly": false
          }
        }
      ]
    }
  ]
}

Test Missing Required Field

fixtures/patient-no-name.json
{
  "resourceType": "Patient"
}

If the profile requires name, assert the server rejects:

{
  "assert": {
    "description": "Patient without required name rejected",
    "responseCode": "422",
    "warningOnly": false
  }
}

Assert OperationOutcome

When a server returns 4xx or 5xx, it should include an OperationOutcome. Assert its presence:

{
  "assert": {
    "description": "Response includes OperationOutcome",
    "resource": "OperationOutcome",
    "warningOnly": false
  }
}

Assert the error severity:

{
  "assert": {
    "description": "OperationOutcome has error issue",
    "expression": "OperationOutcome.issue.where(severity='error').count() > 0",
    "warningOnly": false
  }
}

Test Conflicting Update

Test optimistic locking with If-Match:

{
  "operation": {
    "type": { "code": "update" },
    "resource": "Patient",
    "params": "/${patientId}",
    "sourceId": "patient-update",
    "responseId": "conflictResponse",
    "requestHeader": [
      {
        "field": "If-Match",
        "value": "W/\"outdated-version\""
      }
    ]
  }
}
{
  "assert": {
    "description": "Optimistic lock conflict returns 409",
    "responseCode": "409",
    "warningOnly": false
  }
}

warningOnly vs Strict

Setting Behaviour
"warningOnly": false Test fails if assertion fails — use for conformance requirements
"warningOnly": true Warning logged, test continues — use for optional behaviour

Use warningOnly: false for all negative tests to ensure strict conformance checking.

Next Steps