ts-rest 的 API 測試手法(1)
今天與朋友討論到 ts-rest 撰寫 API test 的時候可以用 Zod 的 safeParse().success 判斷 API Response 的 schema 正不正確。
Sample Code:
const { body, status } = client.users.create({
body: createUserPayload(),
})
expect(
userContract.create.responses[201].safeParse(body)
.success,
).toBe(true)這樣做確實會減少許多傳統需要手動添加 expect.any(Type)...等等的型別驗證,如果只是單純驗證型別,確實透過 ts-rest 合約搭配的 Zod 的 safeParse 安全型別驗證就能夠減少用 expect.any(Type) 去判斷欄位型別是否正確,尤其欄位多達數以十/百計的時候,更能夠顯著提升測試代碼的可讀性。
const { body, status } = await client.users.create({
body: createUserPayload(),
})
expect(body).toEqual<typeof body>({
account: expect.any(String),
// ...other property type or value validate
})但依我個人的淺見是:
如果只是單純驗證欄位型別
在 ts-rest 的 api test 其實可以省略這個步驟。
以 Nest.js為例,如果在有設定 validateResponses: true的情況下,那 API Response 的 Schema 的 Type 如果不符合合約定義,則會自動拋出 Exception。
這樣我認為就不必多做 schema 的驗證(有點多此一舉),測試目標放在資料是否正確以及系統行為是否符合預期才是重點。

不過這樣在撰寫測試時,就得要有一個預設認知:
1. 合約定義一定是對的
2. API 實作一定會照合約走
這些細節一定要和團隊溝通過後再選擇合適的做法哦~
以下是我個人常用的測試手法
const payload = createUserPayload()
const { body, status } = await client.users.create({
body: payload
})
// 以 status 驗證是否成功
if (status !== 201) throw new Error()
// 可以省略 schema validate (if validateResponses: true enabled)
// 以下幾種測試策略,可以用來提升"確認對此功能是否完成的信心度"、KPI 是否達標(X
// 驗證回應資料:API return 的欄位值是不是正確的
expect(body.account).toBe(p.account)
// 驗證系統行為:發個 GET API 測看看資料有沒有真的能從資料庫撈出來了
const { body: getUsersBody, status: getUsersStatus } =
await client.users.getAll()
expect(body.find(i => i.account === p.account)).toBeTruthy()至於第 7 行為何不用expect(status).toBe(201)而是選擇 throw Error 呢... 那是因為我的 ide 沒有那麼聰明。
如果合約定義了不同的 status 會出現不同格式的 body,且又要判斷資料欄位是否正確,那麼在第 12 行的expect(body.account).toBe(p.account)就會出現TS2339 Error-沒有屬性 account。