🔥

Hono + Prisma + PrismockでDBなしでテストを実行する

2025/01/26に公開

皆さんPrisma使ってますか!

この記事ではHono + Prismaの構成にPrismockprisma-fabbricaいうライブラリを使ったテストの例を紹介します
PrismockはPrismaをインメモリ上のDBでエミュレートするもので、prisma-fabbricaはPrismaでmodelのFactoryを自動生成するものです

説明するよりコードを見たほうが早いと思うため、コードを先に載せます

実践

下記のHandlerのテストコードを書きます

export const taskCreateHandler: AppRouteHandler<CreateRoute> = async (c) => {
  const prisma = c.get('prisma');
  const data = c.req.valid('json');

  const count = await prisma.task.count();
  if (count >= 10) {
    return c.json({ message: 'Maximum number of tasks (10) reached' }, 400);
  }

  const task = await prisma.task.create({
    data: {
      title: data.title,
      completed: false,
    },
  });

  return c.json(task, 201);
};

準備としてテスト用のutlilを作成
test-utils.ts

// ...省略

export const setupTestApp = <R extends AppOpenAPI>(routes: R, options: TestSetupOptions = {}) => {
  const prisma = createPrismaMockClient();
  const env = { ...defaultTestEnv, ...options.env };
  const app = createTestApp(routes, prisma, env);
  // Honoのテストクライアントを生成
  const client = testClient(app);
  // prisma-fabbricaにPrismockのクライアントを渡す
  initialize({ prisma });
  // Prismockにて追加されたreset() テストケース毎にインメモリ上のDBをリセットしている
  beforeEach(() => prisma.reset());

  return {
    prisma,
    app,
    client,
  };

テスト作成

task.create.test.ts

import { defineTaskFactory } from '@/../prisma/src/__generated__/fabbrica';
import tasks from '@/app/task/routes/task.index';
import { setupTestApp } from '@/lib/test-utils';
import { describe, expect, it } from 'vitest';

describe('TaskCreateHandler', () => {
  // この中でPrismockを注入している
  const { client } = setupTestApp(tasks);
  // prisma-fabbricaによるFactory自動生成
  const TaskFactory = defineTaskFactory();

  // ...省略

  describe('上限チェック', () => {
    it('Task数が上限に達している場合はエラー', async () => {
      // Prismockとprisma-fabbricaの合わせ技でインメモリ上にデータ10件生成
      await TaskFactory.createList(10);

      // hono/testingのtestClientでリクエスト
      // Handler内では上記でPrismaのMockを注入しているため、コードそのままでインメモリ上で動く
      const res = await client.tasks.$post({
        json: { title: 'テストTask' },
      });

      expect(res.status).toBe(400);
      // DB立ち上げずにエディタ上でVitestぽちでちゃんと上限10件のテストが動く
      expect(await res.json()).toEqual({
        message: 'Maximum number of tasks (10) reached',
      });
    });

    // beforeEach(() => prisma.reset()); がutilに書いてあるのでこのテストケースではインメモリのDBがリセットされている
    it('Task数が上限未満の場合は作成できる', async () => {
      await TaskFactory.createList(9);

      const res = await client.tasks.$post({
        json: { title: 'テストTask' },
      });

      expect(res.status).toBe(201);
    });
  });
});

コード全体を見たい方は下記のリポジトリを参照してください

(色々コード雑なのは許して、、)

https://github.com/nxsdev/hono-prisma-next-workers-starter

結論

これのなにがいいかって超高速でテストが実行できることと、DBを用意する必要なくテストが実行できることです
テストコード内でPrismaのコードを書く場合は

const { client, prisma } = setupTestApp(tasks);

const test = await prisma.task.findMany();
console.table(test);

みたいに書くとデータが生成されているのが確認できます
こんな感じ

prisma-test

Prismaをモックする方法は公式にもあるのですが、公式ドキュメントの方法でモックしようとしたらかなり煩わしいし、かといってRepository作るほどのものを開発していない場合も多いと思います
Prismockを使えば効率良くテストコードの記述が可能です!

ある程度のものはモックしてくれますが、いくつかモックできないパターンもあるようなので、利用する際は下記を見ると良いと思います

morintd/prismock: A mock for PrismaClient, dedicated to unit testing.

基本的なものは対応しているのですが、raw系とかTypedSQLは無理そう(空が返ってるぽい)

https://github.com/morintd/prismock/blob/c7f8b77c3054501c38f7c937c288d7041df783b8/src/lib/client.ts#L26

無理なやつはPrisma公式の方法で諦めてモック頑張って書きましょう、、

HonoはDrizzleとセットで使われることが多いですが(PrismaはCloudflare Workers環境でセットアップがややこしい)Prismaで効率的に開発するのも良いかなと思います!

※現状Prisma6.2.1ではMiniflare環境で起動時にエラー吐くので下記のshでモンキーパッチあててます

https://github.com/nxsdev/hono-prisma-next-workers-starter/blob/main/fix-dirname.sh

短いですがPrismockの紹介でした!

GitHubで編集を提案

Discussion