💾

Drizzle ORMでテストデータの生成を簡単にする

に公開

はじめに

弊社では、テスト実行時にデータベースをモックせず、Testcontainers を用いて実際のデータベースを使用したテストを行っています。
このようなテストを行うためには、事前にテストデータを準備する必要があります。
しかし、テストデータの作成は煩雑になりがちで、可読性の低下やメンテナンスの手間が増える原因となります。

そこで、テストデータの生成をより簡単にするために、Drizzle ORMを活用したテストデータ生成パッケージ@praha/drizzle-factory を開発しました。
本記事では、@praha/drizzle-factoryの基本的な使い方について紹介します。

この記事が参考になった方は、ぜひリポジトリにスターを付けていただけると嬉しいです!
https://github.com/praha-inc/drizzle-factory

drizzle-seedとの違い

drizzle-seedは、データベースのシードデータを作成するためのDrizzle ORMの公式ツールです。
シード可能な疑似乱数ジェネレーターを用いることで、データ生成プロセスを簡略化しています。

一方、@praha/drizzle-factoryはテストケースを書く際に必要なデータを素早く構築することを目的としています。
drizzle-seedとの大きな違いは、ランダムな値に頼るのではなく、明示的にデータを生成する点に重点を置いていることです。
実際に私たちのチームでは、完全なデータセットをシードするのではなく、各テストケースに必要なデータのみを生成するために活用しています。

@praha/drizzle-factoryの使い方

このセクションでは、@praha/drizzle-factoryの基本的な使い方について説明します。
基本的にはREADMEに記載されている内容そのままです。

インストール

まずはパッケージをインストールしましょう。

npm install -D @praha/drizzle-factory

-Dオプションを付けることで、このライブラリを開発依存(devDependencies)に追加できます。テストや開発環境でのみ使用するライブラリは、dependenciesではなくdevDependenciesにインストールするのが一般的です。

本番環境で利用することは推奨していませんが、もし本番環境で使用する場合は、-Dオプションを削除してdependenciesにインストールしてください。

npm install @praha/drizzle-factory

ファクトリの定義

データを生成するために、defineFactory関数を使用してファクトリを定義します。

import { defineFactory } from '@praha/drizzle-factory';
import { pgTable, text, integer } from 'drizzle-orm/pg-core';

const schema = {
  users: pgTable('users', {
    id: integer().notNull(),
    name: text().notNull(),
  }),
};

const usersFactory = defineFactory({
  schema,
  table: 'users',
  // `sequence`は新しいレコードが生成されるたびにインクリメントされる値です。
  resolver: ({ sequence }) => ({
    id: sequence,
    name: `name-${sequence}`,
  }),
});

データの作成

定義したファクトリを使ってデータを生成できます。

1件のレコードを作成

引数なしでcreateを呼び出すことで、1件のレコードを作成できます。

const user = await usersFactory(database).create();
console.log(user);
/*
{
  id: 1,
  name: 'name-1',
}
*/

複数のレコードを作成

createの引数に数値を渡すことで、任意の数のレコードを作成できます。

const users = await usersFactory(database).create(3);
console.log(users);
/*
[
  { id: 1, name: 'name-1' },
  { id: 2, name: 'name-2' },
  { id: 3, name: 'name-3' },
]
*/

特定の値を指定して作成

createの引数にオブジェクトまたはオブジェクトの配列を渡すことで、生成するレコードの値を指定できます。

const user = await usersFactory(database).create({ name: 'John Doe' });
console.log(user);
/*
{
  id: 1,
  name: 'John Doe',
}
 */

const users = await usersFactory(database).create([{ name: 'John Doe' }, { name: 'Jane Doe' }]);
console.log(users);
/*
[
  { id: 1, name: 'John Doe' },
  { id: 2, name: 'Jane Doe' },
]
 */

関連データの生成

他のファクトリを利用して関連レコードを自動生成することも可能です。

const postsFactory = defineFactory({
  schema,
  table: 'posts',
  resolver: ({ sequence, use }) => ({
    id: sequence,
    userId: () => use(usersFactory).create().then((user) => user.id),
    title: `title-${sequence}`,
  }),
});

createを実行すると、自動的にusersテーブルのレコードも作成されます。

const post = await postsFactory(database).create();
console.log(post);
/*
{
  id: 1,
  userId: 1,
  title: 'title-1',
}
*/

トレイトを使ったバリエーションの作成

トレイトを使用すると、特定の条件に合ったデータを作成できます。

const usersFactory = defineFactory({
  schema,
  table: 'users',
  resolver: ({ sequence }) => ({
    id: sequence,
    name: `name-${sequence}`,
  }),
  traits: {
    admin: ({ sequence }) => ({
      id: sequence,
      name: `admin-${sequence}`,
    }),
  },
});

トレイトを使用してデータを生成する場合は、traitsプロパティから呼び出す必要があります。

const adminUser = await usersFactory(database).traits.admin.create();
console.log(adminUser);
/*
{
  id: 1,
  name: 'admin-1',
}
*/

ファクトリの合成

このままでは、ファクトリを個別にimportする必要があり、やや手間がかかります。
そこで、composeFactoryを使用すると、複数のファクトリを統合し、一つのファクトリから異なるテーブルのデータを簡単に作成できます。

import { composeFactory } from '@praha/drizzle-factory';

const factory = composeFactory({
  users: usersFactory,
  posts: postsFactory,
});

このfactoryを利用すると、異なるテーブルのデータを一括で管理できるようになります。

const user = await factory(database).users.create();
console.log(user);
/*
{
  id: 1,
  name: 'name-1',
}
 */

const post = await factory(database).posts.create({ userId: user.id });
console.log(post);
/*
{
  id: 1,
  userId: 1,
  title: 'title-1',
}
 */

まとめ

@praha/drizzle-factoryを使用することで、テストデータを簡単に作成できるようになります!
Drizzle ORMを利用したプロジェクトにおいて、テストデータの管理を効率化し、可読性の高いテストコードを実現するために@praha/drizzle-factoryをぜひ活用していただけると嬉しいです!

PrAha

Discussion