This release is a pre-release and may not be stable for production use.
fhir-sdc-testkit
Test an SDC Questionnaire and its extraction the way a form is really filled: $populate runs, answers are placed at
each linkId's nesting, then QuestionnaireResponse/$extract runs. With submit=True the QuestionnaireResponse is
saved before extraction and the bundle is submitted after it. Only what a user types (or the frontend calculates) is
faked; everything else runs on
a real server implementing the SDC operations, such as
fhir-sdc.
pip install fhir-sdc-testkit
It is released from the fhir-sdc repository, with the same version as the fhir-sdc release it is tested against.
The client
Everything takes an AsyncFHIRClient rooted at the server's /fhir base, built with dump_resource — fhirpy cannot
serialize pydantic resources without it:
fhir_client = AsyncFHIRClient(
f"{base_url}/fhir",
authorization=authorization,
dump_resource=lambda resource: resource.model_dump(),
)
Usage
import fhirpy_types_r4b as r4b
from fhir_sdc_testkit import answer, launch_context, populate_and_extract
async def test_intake_records_the_address(fhir_client):
patient = await fhir_client.create(r4b.Patient(name=[r4b.HumanName(family="Test")]))
await populate_and_extract(
fhir_client,
"patient-intake",
parameters=launch_context(
{"Patient": patient}, subject=r4b.Reference(reference=f"Patient/{patient.id}")
),
answers={"postal-code": [answer(valueString="75001")]},
submit=True,
)
updated = await fhir_client.get(r4b.Patient, patient.id)
assert updated.address[0].postalCode == "75001"
populate_and_extract returns the QuestionnaireResponse and the bundle $extract returned (None when there is
nothing to extract). Without submit neither is saved, so the bundle can be asserted on directly. With it, the
response is the saved one — saved first, since a Mapping may reference it by id — and the bundle has been submitted.
populate and extract run the two operations on their own and save nothing.
Answers
Keys are linkIds, addressed flatly — groups in between are created for you:
answers={
"country": [answer(valueCoding=r4b.Coding(code="FR"))],
"postal-code": [answer(valueString="75001")],
}
A repeating group takes one answer set per occurrence, filled positionally over the occurrences $populate produced,
appending any that are missing and leaving extra populated ones alone:
answers={
"contacts": [
{"phone": [answer(valueString="+1 555 0100")], "label": [answer(valueString="home")]},
{"phone": [answer(valueString="+1 555 0111")], "label": [answer(valueString="work")]},
],
}
Inside a set, linkIds are flat again. A linkId that does not belong to the group raises, naming it.
Parameters
parameters is required: it is passed to $populate and merged into the $extract call, so anything the server
accepts can go in it. launch_context() builds the SDC $populate input — a resource or a Reference under each
context name, and the optional subject. Pass at least one of them: a $populate reply that is not Parameters
raises ValueError.
parameters=launch_context({"Patient": patient, "Author": r4b.Reference(reference="Practitioner/1")})
A list under a name builds one context with a content part per value, as SDC defines it. fhir-sdc uses only the
first content for now.
Failures
An error response from the server raises fhirpy's exception for its status — OperationOutcome for most 4xx and 5xx
responses. A violated itemConstraint the server checks during $extract and a rejected transaction both arrive this
way; they differ in issue[].code. fhir-sdc reports a constraint's key, and a server's own validation code, such as
invalid, for a transaction. An error or fatal issue in the $extract result and, with submit, a batch entry
the server rejects raise ExtractionError.
with pytest.raises(OperationOutcome) as refused:
await populate_and_extract(fhir_client, "patient-intake", parameters=parameters, answers=answers)
assert refused.value.resource["issue"][0]["code"] == "postal-code-required"
Development
uv sync
uv run pytest
uv run ruff format . && uv run ruff check . && uv run mypy
That suite runs without a server. $populate, constraints and extraction are covered against a running Aidbox and
fhir-sdc by tests/testkit/ in the repository root — ./run_test.sh tests/testkit.
Metadata
Release files for fhir-sdc-testkit 3.0.0a1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| fhir_sdc_testkit-3.0.0a1.tar.gz | 125.4 kB | With provenance | Details for fhir_sdc_testkit-3.0.0a1.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| fhir_sdc_testkit-3.0.0a1-py3-none-any.whl | Python 3 | none | any | Details for fhir_sdc_testkit-3.0.0a1-py3-none-any.whl |
Total release size: 134.4 kB
Release files / fhir_sdc_testkit-3.0.0a1.tar.gz
| Download URL | fhir_sdc_testkit-3.0.0a1.tar.gz |
|---|---|
| Size | 125.4 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
03f444952839e708886382ad811696e741da4aa588a96128421a7d12db191278
|
|
BLAKE2b-256 checksum How to use checksums |
46c077a6eae4bec6d2bfe6d95fa4b34bf63dd1c1ecf20bde72d8b6c57804b990
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 27, 2026.
Transparency logRelease files / fhir_sdc_testkit-3.0.0a1-py3-none-any.whl
| Download URL | fhir_sdc_testkit-3.0.0a1-py3-none-any.whl |
|---|---|
| Size | 9.0 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
40b0750ebbee47b6faed9a75198f60ba3229edaa00b920c0a62fd6951e29a4af
|
|
BLAKE2b-256 checksum How to use checksums |
1083745c5321796cad6398998bba8abd5f44ae3415ba9d1f1f5e785a8b80598b
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 27, 2026.
Transparency logSecurity for fhir-sdc-testkit 3.0.0a1
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for fhir_sdc_testkit-3.0.0a1.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 27, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.