はじめに
筆者はHonoを使ってWebサービスを開発しています。APIハンドラーのテスト方法を調べる中でHonoインスタンスのrequest()メソッドを使えば、HTTPサーバーを起動せずにテストできることを知ったのでその方法を記事にまとめます。
この記事では、Honoインスタンスのrequest()メソッドを使って、VitestからAPIをテストする方法を紹介します。あわせて、HTTPサーバーを起動せずにテストできる仕組みや、テストしやすいファイル構成についても触れます。
HTTPサーバーとHonoアプリの関係
Honoは、Web標準のFetch API(Request/Response)をベースに設計されたWebフレームワークです。HonoアプリはRequestを受け取り、Responseを返します。
今回のようにNode.js上で@hono/node-serverを使う場合、ブラウザやAPIクライアントからのHTTPリクエストは、まず@hono/node-serverが受け取ります。受け取ったリクエストはWeb標準のRequestとしてHonoアプリへ渡され、Honoアプリが返したResponseをもとにHTTPレスポンスが送信されます。
ブラウザ・APIクライアント
│ HTTPリクエスト
▼
┌────────────────────┐
│ HTTPサーバー │
│ @hono/node-server │
└─────────┬──────────┘
│ Requestを渡す
▼
┌────────────────────┐
│ Honoアプリ │
│ Router / Middleware│
│ Handler │
└────────────────────┘
│ Responseを返す
▼
┌────────────────────┐
│ HTTPサーバー │
│ @hono/node-server │
└─────────┬──────────┘
│ HTTPレスポンス
▼
ブラウザ・APIクライアント
テストでは、VitestからHonoアプリへ直接リクエストを渡せます。
┌──────────────┐
│ Vitest │
└──────┬───────┘
│ app.request()
▼
┌────────────────────┐
│ Honoアプリ │
│ Router / Middleware│
│ Handler │
└────────────────────┘
HTTPサーバーは経由しませんが、ルーティング、ミドルウェア、ハンドラーは通常どおり動作します。
サンプル実装
この記事では、HonoとVitestが導入済みであることを前提とします。
ファイル構成は次のような感じにします。
src/
├── app.ts
├── app.test.ts
└── index.ts
Honoアプリを作る
src/app.tsにアプリ本体を定義します。
import { Hono } from 'hono'
export const app = new Hono().get('/api/hello', (c) => {
return c.json({
message: 'Hello Hono!'
})
})
HTTPサーバーの起動処理を分離する
src/index.tsには、HTTPサーバーの起動処理だけを記述します。
import { serve } from '@hono/node-server'
import { app } from './app.js'
serve({
fetch: app.fetch,
port: 3000
})
開発時はindex.tsを実行します。一方、自動テストではapp.tsだけを読み込むため、HTTPサーバーは起動しません。
app.request()を使ううえで、この分離自体が必須というわけではありませんが、アプリの定義とserve()を同じファイルに書くと、テストからアプリをimportした際にサーバーの起動処理まで実行されます。そのため、ここではファイルを分けています。
Vitestでテストする
src/app.test.tsを作成します。
import { describe, expect, test } from 'vitest'
import { app } from './app.js'
describe('APIテスト', () => {
test('GET /api/hello', async () => {
const response = await app.request('/api/hello')
expect(response.status).toBe(200)
await expect(response.json()).resolves.toEqual({
message: 'Hello Hono!'
})
})
})
app.request()はWeb標準のResponseを返します。そのため、通常のfetch()と同じようにステータスコードやJSONを確認できます。
テストできる範囲
app.request()では、次の処理を確認できます。
- ルーティング
- ミドルウェア
- バリデーション
- ハンドラー
- ステータスコードとレスポンス本文
まとめ
Honoでは、app.request()を使うことで、VitestからHTTPサーバーを起動せずにHonoアプリを直接テストできます。
const response = await app.request('/api/hello')
ぜひ参考にしてみてください!