2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

B2BエンジニアがT3 Stackに入門してみたAdvent Calendar 2022

Day 6

t3-stack入門 (5) t3-appにVitestを導入する

Last updated at Posted at 2022-12-05

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

ここまでの変更は以下の通りです。

2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?