最近、Nitroでサーバを実装しているけど、
テスト周りでハマったので、いろいろ調べてみたときの備忘録(*´ω`*)
現状、テストに関するガイドはないっぽい
このディスカッションくらいしかないっぽい。。
Nitro自体のテストコードは用意されているが、
ビルドなども含まれているので、これをそのまま使うのは少し冗長。
test/tests.tsやtest/presets/node.test.tsを読んでいくと、
以下のような形で、ビルドされたサーバコード(.output/server/index.msj
)を起動しているよう。
const entryPath = resolve(ctx.outDir, "server/index.mjs"); const { listener } = await import(entryPath); await startServer(ctx, listener);
なので、これを活用してテストコードを書くようにしてみる。
テストコード
Vitestのテストコードはこんな感じ。
// __tests__/apis.test.ts import { describe, expect, it } from "vitest"; import { fetch as ofetch } from "ofetch"; import { joinURL } from "ufo"; // テスト用のClient Util: baseUrlを省略のため const createClient = (baseUrl: string = "http://localhost:3000") => ({ fetch: (url: string, opts?: RequestInit) => ofetch(joinURL(baseUrl, url.slice(1)), opts), }); describe("APITest", async () => { const ctx = createClient(); it("404", async () => { const res = await ctx.fetch("/not_found"); const data = await res.json(); expect(res.status).toBe(404); expect(data).toEqual({ error: { message: "Error: Cannot find any path matching /not_found." }, }); }); });
そのままだとサーバが起動していないので、エラーになる。
global-setup: サーバの起動
なので、Nitroのテストコードを参考に、
サーバを起動する処理を追加していく。
./.output/server/index.mjs
を参照するので、
ビルド後(nitropack build
)じゃないといけない。
// __tests__/common/global-setup.ts import { resolve } from "pathe"; export async function setup() { const entryPath = resolve("./.output/server/index.mjs"); await import(entryPath); }
サーバの起動は、テスト時に全体で1度だけにしたい。
VitestのglobalSetup
を使って起動するようにしておく。
// vitest.config.ts import { defineConfig } from "vitest/config"; export default defineConfig({ test: { include: ["**/__tests__/**/*.test.ts"], globalSetup: ["./__tests__/common/global-setup.ts"], }, });
beforeAll
などを使うと、複数回起動されてしまうので、
ポートが重複して、起動時にエラーになる。。
Nitroのテストコードでは、
ビルドから行っていて、各テストコードごとに.output/
が異なるようにしているっぽい?
package.jsonのscripts
準備ができたので、scripts
に追加する。
{ "type": "module", "scripts": { // ...略 // for testing "test": "pnpm vitest-es", "vitest-es": "NODE_OPTIONS=\"--enable-source-maps --experimental-vm-modules\" vitest --single-thread --run" }, "dependencies": { "nitropack": "^2.5.2" }, "devDependencies": { "typescript": "^5.1.6", "vitest": "^0.34.1" } }
あとは、pnpm build && pnpm test
などすればOK。
Turborepoとかを使うと、
ビルドをキャッシュしてくれたりするので便利。
Firebase Emulatorに接続してテストする
FirebaseのAdmin SDKを使うことも多いので、
Firebase Emulatorに接続してテストしたい。
エミュレータに接続してもらうために、環境変数を設定しておく。
# .env.test GCLOUD_PROJECT="my-test-project" FIREBASE_AUTH_EMULATOR_HOST="127.0.0.1:9099" FIRESTORE_EMULATOR_HOST="127.0.0.1:8080" FIREBASE_STORAGE_EMULATOR_HOST="127.0.0.1:9199" FIREBASE_DATABASE_EMULATOR_HOST="127.0.0.1:9000"
あとは、firebase emulators:exec
とenv-cmd
を使って、
エミュレータを起動しつつ、テストを実行するようにすればOK
{ "type": "module", "scripts": { // ...略 // for testing "test": "env-cmd -f .env.test pnpm em:run 'pnpm vitest-es'", "em:run": "firebase emulators:exec --only auth,firestore --project=my-test-project", "vitest-es": "NODE_OPTIONS=\"--enable-source-maps --experimental-vm-modules\" vitest --single-thread --run" }, "dependencies": { "nitropack": "^2.5.2" }, "devDependencies": { "typescript": "^5.1.6", "vitest": "^0.34.1", "env-cmd": "^10.1.0", "firebase-admin": "^11.10.1" } }
以上!! これで一通りは準備が整った気がする。。(*´ω`*)