Contract testing with OpenAPI & TypeScript

Alex O'Callaghan

使用 OpenAPI 與 TypeScript 進行契約測試

原文由 Alex O'Callaghan 發布,訂閱此部落格

在擁有多個軟體開發團隊協作的大型組織中,系統之間的跨邊界互動往往是故障發生的關鍵點。建立定義清晰的 API 並就其運作方式達成共識,有助於大幅減少整合錯誤。透過 OpenAPI 與 TypeScript 這類工具,就能提供可藉由自動化測試來驗證的清晰定義。

情境

想像一個情境:一個工程團隊正在實作 Python 後端服務,同時與另一個負責開發網頁應用程式的團隊緊密合作,該應用程式將使用此服務來實現部分功能。雙方已就系統需求與 API 的運作方式達成共識,但隨著時間推移,越來越多團隊成為這個服務的消費者,功能不斷被新增與修改,錯誤也隨之產生。

我們該如何加入自動化測試來協助捕捉整合問題?端對端整合測試是個不錯的想法,但也有一些缺點。要啟動整個技術堆疊,需要在工具選擇上達成一致,而且測試執行起來可能很慢。再加上多個消費者,這類建置需要投入大量的工程時間與心力來維護。

契約測試,也就是驗證對服務發出的請求是否回傳預期的狀態碼與資料結構,能在技術選型上只需最低程度共識的情況下,協助捕捉未被察覺的錯誤與破壞性變更。

使用 OpenAPI 與 TypeScript

OpenAPI 是用於描述 API 的熱門標準,相容於 JSON Schema 並支援眾多框架。TypeScript 則是為 JavaScript 應用程式導入靜態型別的熱門選擇。

當後端服務提供了 OpenAPI 的 YAML 或 JSON 綱要,前端應用程式就能使用 openapi-typescript 來為該服務的 API 產生型別定義:

$ npx openapi-typescript https://service.dev/openapi.json -o ./schema.d.ts

這些型別可以在發送請求與處理回應時手動引用,或搭配像 openapi-fetch 這類客戶端來提供具型別安全的請求。

執行 tsc --noEmit 即可進行型別檢查,找出任何問題。

與 CI 整合

在前端專案中,做法相當直觀:新增一個 CI 步驟,從正式環境的服務拉取最新的 OpenAPI 規格,用 openapi-typescript 產生型別定義,然後以 tsc 執行型別檢查。

至於後端專案,如果你的 CI 平台有支援,你可以在消費者的專案中觸發管線。舉例來說,使用 GitLab CI's multi-project pipelines

# Backend service

## Generate `openapi.json` and store as an artifact
generate-openapi:
  stage: test
  artifacts:
    paths:
      - openapi.json
    when: always
  script:
   - make generate-openapi

## Trigger a downstream pipeline in consumer
validate-openapi:
  stage: test
  needs: ["generate-openapi"]
  variables:
    UPSTREAM_REF: $CI_MERGE_REQUEST_REF_PATH
  trigger:
    project: path/to/consumer
    branch: master
    strategy: depend

# Frontend service
## Generate types from `openapi.json` schema and use `tsc` to type check
validate-openapi:
  stage: test
  needs:
   - project: path/to/service
     job: generate-openapi
     ref: $UPSTREAM_REF
     artifacts: true
  rules:
    - if: $CI_PIPELINE_SOURCE == "pipeline"
  script:
    - npx openapi-typescript openapi.json --output path/to/schema.ts
    - tsc --noEmit

如此一來,後端服務的任何變更,現在都會在所有消費者應用程式中被驗證是否有破壞性變更。

總結

在理想的世界裡,你會擁有一支能完整維護自身技術堆疊的跨職能團隊。在大型組織中,情況往往並非如此,跨團隊協作的協調也充滿挑戰。良好的溝通固然是關鍵,但自動化的契約測試能協助捕捉那些原本會流入正式環境的問題。即使是由單一團隊維護的單一服務與單一消費者,隨著系統複雜度提升,API 的變更也可能變得難以推斷。OpenAPI 與 TypeScript 是提升開發體驗的絕佳工具,能以相當簡單的方式,在系統之間建立起穩固且可測試的契約。

本文章由 muse-spark-1.2-contributor 進行翻譯

留言