Skip to main content

Claims Service — Testing Strategy

Status: populated Owner: TBD Last updated: 2026-04-18 Companion: SERVICE_OVERVIEW · Service Template · 02 DDD

Coverage Targets

LayerTarget
Unit (domain + application)90% line coverage
Integration80% branch coverage
Contract (Pact + FHIR)All consumer contracts passing
E2ECritical billing workflows covered

Unit Tests

Domain and application layer tests run in-process with no external dependencies.

Domain tests:

  • ClaimRecord.assemble() — valid inputs produce draft claim
  • ClaimRecord.validate() — missing required fields throws validation error
  • ClaimRecord.submit() — transitions draft→submitted correctly
  • ClaimRecord.submit() — throws if status is not ready
  • ClaimRecord.applyRemittance() — transitions accepted→paid with correct amounts
  • ClaimRecord.applyRemittance() — partial payment sets status to partial_paid
  • ClaimRecord.deny() — transitions accepted→denied, creates DenialCase
  • ClaimRecord.appeal() — transitions denied→appealed, sets appeal deadline
  • Coverage.activate() / Coverage.deactivate() — status transitions
  • Money calculations — amount arithmetic with currency consistency check
  • State machine invariants — all invalid transitions raise errors

Application use-case tests:

  • AssembleClaimUseCase — happy path produces claim and outbox event
  • AssembleClaimUseCase — invalid coding raises CODING_INVALID
  • AssembleClaimUseCase — duplicate claim raises DUPLICATE_CLAIM
  • SubmitClaimUseCase — happy path calls adapter, updates status
  • SubmitClaimUseCase — adapter failure raises SUBMISSION_FAILED
  • ProcessRemittanceUseCase — happy path generates EOBs and events
  • VerifyEligibilityUseCase — active coverage returns eligibility result
  • VerifyEligibilityUseCase — adapter timeout → error stored, event emitted

Mandatory Integration Tests

These tests run against a real Postgres (testcontainers) and real NATS.

TestDescription
tenant-isolationClaim created by tenant A is invisible to tenant B — asserts RLS policy
cross-tenant-coverageCoverage belonging to tenant A cannot be referenced in tenant B claim
outbox-relayClaim submitted → outbox record written → relay publishes event to NATS
version-conflictConcurrent update with stale version returns 409
eligibility-cache-expiryExpired eligibility result returns ELIGIBILITY_EXPIRED error
era-idempotencyProcessing the same ERA twice is a no-op (no duplicate allocations)
module-not-licensedClaim creation returns 403 when tenant lacks ehr.claims entitlement
fhir-eob-generatedERA processing creates FHIR ExplanationOfBenefit resource
appeal-deadlineAppeal filed after deadline raises CLAIM_INVALID_STATE

Contract Tests

Consumer-Driven (Pact):

  • billing-service → claims-service: POST /api/v1/claims with charge IDs
  • patient-portal-service → claims-service: GET /fhir/R4/ExplanationOfBenefit?patient={id}
  • population-health-service → claims-service: consumes claims.claim.denied.v1 event

FHIR Golden Fixture Tests:

  • Claim resource conforms to FHIR R4 base profile
  • Coverage resource conforms to FHIR R4 base profile
  • ExplanationOfBenefit resource passes FHIR R4 validation
  • CoverageEligibilityResponse resource conforms to FHIR R4 base profile

E2E Scenarios (Billing Workflows)

ScenarioDescription
Full claim lifecycleAssemble → scrub → submit → receive ERA → apply payment → EOB visible
Denial and appealClaim denied → appeal filed → appeal approved → claim paid
Coverage verificationAdd coverage → verify eligibility → attach to encounter → submit claim
Secondary billingPrimary claim paid → secondary claim created with COB adjustment
ERA multi-claimSingle ERA containing allocations for 5 claims — all correctly applied

Adapter / Clearinghouse Mocking

Integration and E2E tests use a stub EDI adapter (StubClaimSubmissionAdapter) that:

  • Returns a synthetic clearinghouseRef on submit()
  • Returns a synthetic 999 acknowledgement after 100ms
  • Supports configurable failure scenarios via test fixture injection

SFTP-based adapters are tested with an embedded SFTP server (Apache SSHD).