4. FHIR接口功能测试

FHIR接口功能测试的核心,是围绕资源生命周期与交互语义逐项验证服务器行为是否符合规范。本节以 Patient 资源为主线,覆盖创建与读取、搜索参数、分页与排序、条件更新与删除、批量操作五类测试,并给出可复用的测试用例与判读依据。

一、创建与读取:从 POST 到条件读取

创建 Patient 使用 POST [base]/Patient,请求头必须包含 Content-Type: application/fhir+json,可选 Prefer: return=representation 让服务器回传完整资源。示例请求体:

{
  "resourceType": "Patient",
  "identifier": [{"system": "http://hospital.example/mrn", "value": "MRN-10086"}],
  "name": [{"family": "张", "given": ["三"]}],
  "gender": "male",
  "birthDate": "1985-07-12"
}

成功时返回 201 Created,响应头 Location: [base]/Patient/123/_history/1,正文为创建后的资源(含服务器分配的 id 与 meta.versionId)。若服务器不支持 return=representation,可能返回 201 且正文为空,此时必须通过 Location 头解析出 id 再读取。

读取分三种:GET /Patient/123 读当前版本;GET /Patient/123/_history/1 读指定版本;GET /Patient?identifier=... 做条件读取。测试要点:读取不存在的 id 应返回 404 且 OperationOutcome 中 issue.code 为 not-found;读取已删除资源应返回 410 Gone;条件读取命中 0 条返回 404,命中多条返回 412 Precondition Failed。这些状态码是判断服务器实现是否严谨的关键。

二、搜索参数验证

搜索测试要覆盖参数类型与修饰符。以 Patient 为例,常用参数包括 name(string)、identifier(token)、birthdate(date)、gender(token)、active(token)、address(string)、organization(reference)。验证时需构造能命中与不能命中的数据,检查 Bundle.total 与 entry 数量是否一致。

修饰符测试同样重要:name:exact 要求精确匹配;identifier:of-type 按编码系统匹配;birthdate=ge1985-01-01&birthdate=le1985-12-31 验证范围组合;gender:missing=true 验证缺失值查询。日期参数必须支持前缀 eq/ne/gt/lt/ge/le,且时间精度要与资源中存储精度兼容。若服务器返回 400 并提示未知参数,说明其未实现该参数或未开启严格模式,需在测试报告中记录。

FHIR Patient 接口功能测试链路 测试客户端 REST / JSON FHIR 服务器 Patient 资源库 ① 创建 POST 201 + Location ② 读取 GET 200 / 404 / 410 ③ 搜索 GET Bundle + total ④ 分页与排序 _count=10 · _sort=-date · link[next] Bundle.link[relation=next/self] ⑤ 条件更新 / 删除 PUT ?identifier= · If-Match DELETE → 204 / 410 ⑥ 批量操作测试 Batch:独立事务,逐条返回 entry.response.status Transaction:原子提交,全部成功或全部回滚 关键判读依据 • 状态码:201/200/204/400/404/410/412 是否符合预期 • 响应头:Location、ETag、Content-Type 是否完整 • Bundle:total、entry、link[next] 是否一致 • OperationOutcome:issue.code 与 severity 是否合理

三、分页与排序

搜索返回的 Bundle 必须包含 link 数组,其中 relation=self 表示当前页 URL,relation=next 表示下一页。测试时先请求 GET /Patient?_count=10&_sort=-birthdate,检查 Bundle.total 是否等于实际匹配总数,entry 数量是否不超过 10。随后跟随 next 链接继续请求,直到没有 next 为止,统计所有页 entry 之和是否等于 total。

排序验证要关注多字段与方向:_sort=family,-birthdate 表示先按 family 升序、再按 birthdate 降序。服务器若不支持某字段排序,应返回 400 或忽略该字段,测试需记录实际行为。分页边界包括:_count=0 应返回 total 但无 entry;_count 超过服务器上限时应被截断或返回 400;最后一页不应出现 next 链接。

四、条件更新与删除

条件更新使用 PUT /Patient?identifier=http://hospital.example/mrn|MRN-10086,请求体为完整 Patient 资源。若命中唯一资源,服务器执行更新并返回 200 或 201;命中 0 条时,若服务器支持条件创建则返回 201,否则返回 404;命中多条返回 412。更新时应携带 If-Match: W/"1" 做乐观锁校验,版本不匹配返回 412,这是并发安全的关键测试点。

条件删除使用 DELETE /Patient?identifier=...,成功返回 204,再次读取返回 410。删除后 _history 中应保留删除记录,且 meta.versionId 递增。测试需验证:删除不存在的资源返回 404;重复删除返回 410 或 204(取决于服务器实现,需记录);删除后搜索不应再返回该资源。

五、批量操作测试

批量操作通过 POST [base] 提交 Bundle,type 为 batch 或 transaction。Batch 中每个 entry 独立执行,互不影响,响应 Bundle 中每个 entry 的 response.status 单独反映成功或失败。Transaction 要求原子性:任一 entry 失败则全部回滚,且 entry 之间可通过 fullUrl 与 urn:uuid 引用建立依赖。

测试用例设计:构造一个 batch,包含 1 个 POST Patient、1 个 GET 不存在的 Patient、1 个 PUT 更新已有 Patient。预期响应中三个 entry 的 status 分别为 201、404、200,且 batch 整体返回 200。再构造一个 transaction,其中第二个 entry 引用第一个 entry 创建的 Patient,验证服务器是否正确解析引用并原子提交。若 transaction 中任一 entry 失败,检查是否所有 entry 均未持久化。

建议将上述五类测试组织为可重复执行的测试套件:每个用例记录请求方法、URL、请求体、预期状态码、预期响应头与断言条件。执行后对比实际响应,将差异分为“规范不符”“实现差异”“环境问题”三类,便于后续一致性验证阶段定位问题。
注意:不同 FHIR 服务器对条件更新命中多条、重复删除、_count 上限等边界行为存在实现差异。测试报告应明确记录服务器版本与配置,避免将实现差异误判为规范缺陷。