t3-appにテスト関連の設定は一切書かれていません。潔いですね。
急いでUnitTest用のフレームワークを導入することとします。
何を導入するか
テストを導入するとすると第一選択肢はJestなんでしょうが、
trpcのexamples-next-prisma-starterを見てみるとより高速なVitestが使われているようです。
Testは早いに越したことはありません。
Jestもesbuil等を使えば早くなるみたいですが、とりあえずexamplesで使われているVitestを入れてみます。
vitestの設定
Vitestをインストール。
~/t3-app-example$ npm install -D vite vitest
vitest.config.tsを追加します。とりあえずexamplesのものをそのままコピー
import { fileURLToPath } from "url";
import { configDefaults, defineConfig } from "vitest/config";
export default defineConfig({
test: {
globals: true,
exclude: [...configDefaults.exclude, "**/playwright/**"],
alias: {
"~/": fileURLToPath(new URL("./src/", import.meta.url)),
},
},
});
testの作成
src/server/trpc/router/todo.test.tsにテストを書きます。とりあえずダミーのテストを追加。
import { describe, expect, it } from "vitest";
function add(lhs: number, rhs: number) {
return lhs + rhs;
}
describe("add", () => {
it("1 + 2 = 3", () => {
const result = add(1, 2);
expect(result).toBe(3);
});
});
vitestを実行
~/t3-app-example$ npx vitest
DEV v0.25.3 /home/uehara/t3-app-example
✓ src/server/trpc/router/todo.test.ts (1)
Test Files 1 passed (1)
Tests 1 passed (1)
Start at 22:53:34
Duration 966ms (transform 528ms, setup 0ms, collect 24ms, tests 4ms)
PASS Waiting for file changes...
press h to show help, press q to quit
テスト実行まではできているようなので、todoRouterのテストを作成します。
import { test, expect } from "vitest";
import { createContextInner } from "../context";
import type { AppRouter } from "./_app";
import { appRouter } from "./_app";
import type { inferProcedureInput } from "@trpc/server";
test("create and getAll todos", async () => {
const ctx = await createContextInner({
session: {
user: {
id: "id12345",
name: "testUser",
email: "test@test.com",
},
expires: "1234567",
},
});
const caller = appRouter.createCaller(ctx);
const input: inferProcedureInput<AppRouter["todo"]["create"]> = {
title: "Add Unit test",
description: "Add Unit test by vitest",
};
const post = await caller.todo.create(input);
const posts = await caller.todo.getAll();
expect(posts.length).toBe(1);
expect(post.id).toBeGreaterThan(0);
});
が、今のままだとテストが実行されるたびにdbにTodoが追加されるので、複数回実行すると当然エラーになります。
~/t3-todo-example$ npx vitest
DEV v0.25.8 /home/uehara/t3-todo-example
❯ src/server/trpc/router/todo.test.ts (1)
× create and getAll todos
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯ Failed Tests 1 ⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯
FAIL src/server/trpc/router/todo.test.ts > create and getAll todos
AssertionError: expected 2 to be 1 // Object.is equality
❯ src/server/trpc/router/todo.test.ts:28:24
26| const post = await caller.todo.create(input);
27| const posts = await caller.todo.getAll();
28| expect(posts.length).toBe(1);
| ^
29| expect(post.id).toBeGreaterThan(0);
30| });
- Expected "1"
+ Received "2"
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[1/1]⎯
Test Files 1 failed (1)
Tests 1 failed (1)
Start at 15:42:51
Duration 1.54s (transform 652ms, setup 0ms, collect 454ms, tests 56ms)
FAIL Tests failed. Watching for file changes...
press h to show help, press q to quit
テスト用データベースのリセット方法
どうしようか迷ったんですが、こちらの記事を参考にworkerごとに接続先を変える。だたし今使っているのはsqliteなので、データベースのresetはnpx prisma migrate reset
を使うのではなく、dbファイルをコピーすることにしました。
vitest.config.tsにvitest.setup.tsを読み込むように設定します。
},
+ setupFiles: "./vitest.setup.ts",
},
});
vitest.setup.tsでworkerごとに接続先を変え、その接続先のファイルに、resetされたデータベースファイルをコピーすることにします。
import process from "process";
import fs from "fs";
import { beforeAll, afterAll } from "vitest";
// DATABASE_URL には 'mysql://user:password@localhost:3306/db' のような文字列が入っている想定。
process.env.DATABASE_URL = `file:./db.sqlite-test-${process.env.VITEST_WORKER_ID}`;
beforeAll(async () => {
await fs.copyFileSync(
"prisma/reset.sqlite",
`prisma/db.sqlite-test-${process.env.VITEST_WORKER_ID}`
);
});
afterAll(async () => {
fs.unlinkSync(`prisma/db.sqlite-test-${process.env.VITEST_WORKER_ID}`);
});
これで、Workerごとにresetされたデータベースが用意されるようになりました。
sqlite以外を使う場合はnpx prisma migrate reset
を使うしかないのでしょうか。
postgresql,mysqlもsqliteみたいに1つのファイルでデータベースが起動するモードを
作ってくれると楽なんですが。
~/t3-app-example$ npx vitest
DEV v0.25.3 /home/uehara/t3-app-example
stdout | src/server/trpc/router/todo.test.ts > create and getAll todos
1
✓ src/server/trpc/router/todo.test.ts (1)
Test Files 1 passed (1)
Tests 1 passed (1)
Start at 23:51:58
Duration 1.48s (transform 661ms, setup 21ms, collect 362ms, tests 51ms)
PASS Waiting for file changes...
press h to show help, press q to quit
ここまでの変更は以下の通りです。