vitest에서 사용자 지정 assertion을 추가하여 Zod 스키마와 Response 객체를 비교하기

// vitest 5.0 기준
import { expect } from 'vitest'
import type { MatcherResult } from 'vitest'
import type { ZodTypeAny } from 'zod'

expect.extend({
  /**
   * @param received 테스트할 Response 객체
   * @param schema 검증할 Zod 스키마
   */
  async toMatchSchema(received: Response, schema: ZodTypeAny) {
    const response = await received.json()
    const result = await schema.safeParseAsync(response)

    return {
      message: () => '',
      pass: result.success,
    } satisfies MatcherResult
  },
})

vitest.d.ts 에서 vitest 모듈의 Matchers 인터페이스를 확장한다. 비동기 매처라 반환 타입은 R 이 아니라 Promise<void>1.

import 'vitest'
import type { ZodTypeAny } from 'zod'

declare module 'vitest' {
  interface Matchers<R, T> {
    toMatchSchema: (schema: ZodTypeAny) => Promise<void>
  }
}

todoResponse의 응답 데이터가 todoSchema에 정의된 Zod 스키마와 일치하는지 확인한다. 매처가 async라 테스트에서 await 한다1.

test('todo', async () => {
  expect(todoResponse.ok).toBeTruthy()
  await expect(todoResponse).toMatchSchema(todoSchema)
})

Footnotes

  1. Extending Matchers | Guide | Vitest: “If a matcher implementation is asynchronous, declare its return type as Promise<void> instead of R and don’t forget to await it in the test” ↩ ↩2

#317