DBがPostgreSQLなHonoアプリをPGliteでテストする

2024/12/31に公開

素朴にPostgreSQLに繋いでデータ拾ってくるとこんな感じ?

app.ts
import { Hono } from "hono";
import { drizzle } from "drizzle-orm/node-postgres";
import pg from "pg";
import * as schema from "/path/to/schema";

const pool = new pg.Pool({
  connectionString: process.env.DATABASE_URL,
});
const db = drizzle(pool, { schema });

export const app = new Hono().get("/todos", async (c) => {
  const rows = await db.query.todos.findMany();

  return c.json(rows);
});
schema.ts
import { boolean, pgTable, serial, text } from "drizzle-orm/pg-core";

export const todos = pgTable("todos", {
  id: serial("id").primaryKey(),
  title: text("title").notNull(),
  done: boolean("done").notNull().default(false),
});

まずはappを何かしらの方法で生成出来るようにする

app.ts
import { Hono } from "hono";
import { drizzle } from "drizzle-orm/node-postgres";
+ import type { drizzle as pglite } from "drizzle-orm/pglite";
import pg from "pg";
import * as schema from "/path/to/schema";

const pool = new pg.Pool({
  connectionString: process.env.DATABASE_URL,
});
const db = drizzle(pool, { schema });

- const app = new Hono().get("/todos", async (c) => {
-   const rows = await db.query.todos.findMany();
- 
-   return c.json(rows);
- });

+ type DB =
+   | ReturnType<typeof drizzle<typeof schema>>
+   | ReturnType<typeof pglite<typeof schema>>; 
+
+ export function createApp(db:DB){
+   return new Hono()
+     .get("/todos",async(c)=>{
+       const rows = await db.query.todos.findMany();
+
+       return c.json(rows);
+     })
+ }
+
+ export const app = createApp(db);
...

テストの準備をする

setup.ts
import { PGlite } from "@electric-sql/pglite";
import { sql } from "drizzle-orm";
import { drizzle } from "drizzle-orm/pglite";
import { migrate } from "drizzle-orm/pglite/migrator";
import { afterAll, afterEach, beforeAll } from "vitest";
import * as schema from "/path/to/schema";

let pglite: PGlite;
export let testDB: ReturnType<typeof drizzle<typeof schema>>;
const migrationsFolder = "drizzle/migrations"

beforeAll(async () => {
  pglite = new PGlite();
  testDB = drizzle(pglite, { schema });

  await migrate(testDB, { migrationsFolder });
  await seed();
});

afterEach(async () => { // テストが終わる度にDBを初期化する
  await testDB.execute(sql`drop schema if exists public cascade`);
  await testDB.execute(sql`create schema public`);
  await testDB.execute(sql`drop schema if exists drizzle cascade`);
  await testDB.execute(sql`create schema drizzle`);
  await migrate(testDB!, { migrationsFolder });
  await seed();
});

afterAll(async () => {
  if (pglite) {
    await pglite.close();
  }
});

async function seed() { // 初期データ
  await testDB.insert(schema.todos).values([
    {
      id: 1,
      title: "test1",
      done: false,
    },
    {
      id: 2,
      title: "test2",
      done: true,
    },
  ]);
}

取得のテストを書く

app.test.ts
import { testClient } from "hono/testing";
import { test, expect } from "vitest";
import { testDB } from "/path/to/setup";
import { createApp } from "/path/to/index";

test("GET tasks", async () => {
  const app = createApp(testDB);
  const client = testClient(app);

  const res = await client.todos.$get();
  const json = await res.json();

  expect(res.json.length).toEqual(2);
  ...
});

後はテスト実行

$ npx vitest run

速い!

Discussion