2. FHIR资源与交互基础
FHIR 的互操作性建立在“资源”这一最小可交换单元之上。理解 FHIR 接口测试,第一步不是急着发请求,而是先看清一个资源由什么构成、字段如何取值、资源之间如何互相指向,以及服务端通过哪些交互把这些资源暴露出来。本节围绕资源结构、数据类型、引用机制、CRUD 与搜索交互,以及 CapabilityStatement 的作用展开,为后续的一致性验证建立共同语言。
资源结构:从 Resource 到具体资源
所有 FHIR 资源都继承自抽象类型 Resource,因此每个资源都具备一组公共元素:id(资源在服务端内的逻辑标识)、meta(版本、最后更新时间、安全标签、profile 声明)、implicitRules、language,以及 text(供人阅读的叙述性内容)。在此之上,具体资源再定义自己的领域字段,例如 Patient 有 identifier、name、gender、birthDate,Observation 有 status、code、subject、value[x]、effective[x]。
资源内部大量使用“元素(Element)”和“反向引用(BackboneElement)”来组织嵌套结构。例如 Patient.name 是一个可重复的 HumanName 元素,其下又有 family、given、use 等子元素。测试时若只校验顶层字段存在,很容易漏掉嵌套元素的基数约束,因此需要按路径逐层核对。
数据类型:简单类型与复杂类型
FHIR 数据类型分为简单类型和复杂类型。简单类型包括 boolean、integer、decimal、string、uri、code、dateTime、instant 等;复杂类型包括 CodeableConcept、Coding、Quantity、Period、Reference、Identifier、HumanName、Address 等。区分它们对测试非常关键:code 只能取固定值集中的值,而 CodeableConcept 可以携带多个 Coding 以及自由文本 text。
以 Observation.code 为例,它通常是 CodeableConcept,可以同时包含 LOINC 编码和本地编码。测试用例中应分别验证:coding.system 是否为合法 URI、coding.code 是否在值集内、text 是否与编码语义一致。日期时间类型也要注意精度与格式,dateTime 允许只到日、月或年,而 instant 必须精确到秒并带时区。
引用机制:资源之间如何连接
FHIR 用 Reference 类型表达资源之间的指向关系。引用有三种常见形式:字面引用(reference: "Patient/123")、绝对引用(reference: "https://example.org/fhir/Patient/123")和逻辑引用(通过 identifier 指向业务标识)。此外还有 display 字段用于展示,以及 type 字段声明目标资源类型。
引用是接口测试中最容易出问题的地方。常见缺陷包括:引用了不存在的资源、引用类型与目标资源不匹配、只给 display 而不给 reference、跨版本引用未做版本化处理。测试时应构造“悬空引用”用例,验证服务端是否返回 422 或 400,并检查 Bundle 中是否包含被引用资源。
CRUD 与搜索交互
FHIR 的 RESTful 交互以资源类型和实例 URL 为基础。创建用 POST /Patient,成功返回 201 Created 并在 Location 头中给出新资源地址;读取用 GET /Patient/123,返回 200 OK 或 404 Not Found;更新用 PUT /Patient/123,需携带完整资源或使用 PATCH 做局部修改;删除用 DELETE /Patient/123,通常返回 204 No Content。测试时要特别关注条件更新(If-Match)和乐观锁,避免并发覆盖。
搜索是 FHIR 最复杂也最常被测试的部分。搜索参数分为字符串、令牌、日期、数量、引用、复合等类型。例如 GET /Patient?name=张&birthdate=ge1990-01-01 会返回一个 Bundle,其 type 为 searchset,entry 中每个元素包含 resource 和 search.mode。分页通过 link 中的 next、self、previous 实现。若需要同时取回被引用资源,可使用 _include=Observation:patient 或 _revinclude。测试用例应覆盖:无结果时返回空 Bundle 而非 404、非法参数返回 400 并带 OperationOutcome、分页链接可继续请求。
CapabilityStatement 的作用
CapabilityStatement 是服务端对自身能力的“自描述声明”,它告诉客户端:支持哪些 FHIR 版本、哪些资源、每个资源支持哪些交互和搜索参数、是否支持批量操作、安全机制是什么。它通常通过 GET /metadata 获取。对接口测试而言,CapabilityStatement 是生成测试用例的重要依据:声明支持的交互必须实际可用,声明不支持的交互应返回 405 或 404,声明的搜索参数应能被正确解析。
一致性验证时,可以把 CapabilityStatement 当作“契约”来比对。例如声明 Patient 支持 read、search-type、create,那么测试就应覆盖这三类交互;若声明了 searchParam 为 birthdate,则应验证该参数确实生效。反之,若实际行为与声明不符,就是典型的一致性缺陷。
rest.resource 数组,逐项提取 type、interaction.code、searchParam.name,再据此生成正向与反向用例。正向用例验证“声明支持即可用”,反向用例验证“未声明即拒绝”。
一个可复用的检查清单
| 检查项 | 关注点 | 典型判断依据 |
|---|---|---|
| 资源结构 | 公共元素与领域字段是否完整 | id、meta、text 是否存在;必填字段基数是否满足 |
| 数据类型 | 简单类型与复杂类型取值是否合法 | dateTime 格式、CodeableConcept 编码系统、Quantity 单位 |
| 引用机制 | 引用是否可解析、类型是否匹配 | 悬空引用返回 422;_include 能带回目标资源 |
| CRUD | 状态码、Location、版本控制 | 201/200/204;If-Match 冲突返回 412 |
| 搜索 | 参数解析、分页、Bundle 结构 | searchset 类型;next 链接可继续;非法参数返回 400 |
| CapabilityStatement | 声明与实际行为是否一致 | 声明支持的交互可用;未声明交互被拒绝 |