database-testing について
database-testingは、エンジニア向けのデータベーステストスキルです。マイグレーション、データ整合性、シードデータ、クエリ性能を扱い、実際の適用・ロールバック手順とインデックス検証を重視します。
主な機能
- 01適用・ロールバックのテスト
- 02制約と参照整合性の検証
- 03決定的なシードデータと分離
- 04マイグレーションのドリフト検出
- 05EXPLAIN計画とインデックスの検証
活用シーン
- 01既存データが移行後も保たれるか確認
- 02制約が不正なレコードを拒否するか検証
- 03スキーマとマイグレーションの不一致を発見
- 04重要なクエリがインデックスを使うか確認
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ petrkindlmann/ qa-skills --skill database-testingインストール後、Claude Code で/database-testingを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
references/integrity-and-seed.mdmigration-tests.mdperformance-and-docker.md詳細
開始前に: .agents/qa-project-context.md を確認し、データベースの種類、ORM、マイグレーションツール、環境設定を把握してください。これらは以下のすべてのパターンに影響します。
調査時の質問
まず .agents/qa-project-context.md を確認してください。ファイルがある場合はその内容を使用し、すでに回答されている項目は飛ばします。そのうえで、次の点を確認してください。
- データベースの種類: PostgreSQL、MySQL、SQLite、MongoDB、それとも複数のデータベースですか?制約の構文、マイグレーションツール、パフォーマンスのプロファイリングは種類ごとに異なります。
- ORM/クエリビルダー: Prisma、TypeORM、Drizzle、Sequelize、SQLAlchemy、Django ORM、それとも生の SQL ですか?ORM によってマイグレーションツールとテストのパターンが決まります。
- マイグレーションツール: Prisma Migrate、TypeORM migrations、Flyway、Liquibase、Alembic、knex、それとも独自のツールですか?前進・ロールバック両方のマイグレーションをテストする方法が決まります。
- テストデータベースの戦略: テストごとに独立したデータベースを使いますか、トランザクションをロールバックしますか、Testcontainers を使いますか、それとも共有データベースをクリーンアップして使いますか?速度と信頼性に影響します。
- 既存のシードデータ: ファクトリー、フィクスチャ、シードスクリプトはありますか?
prisma/seed.ts、seeds/、fixtures/、またはファクトリーのパターンを確認してください。 - パフォーマンスのベースライン: 既存のクエリベンチマークやスロークエリのモニタリングはありますか?
基本原則
-
マイグレーションは前進・ロールバックの両方をテストします。 すべてのマイグレーションは元に戻せる必要があります。ロールバックに失敗すると、問題のあるデプロイから復旧できません。
upだけでなくdownのパスもテストしてください。メタデータのフラグではなく、実際の取り消し方法(Prisma の場合は手書きのdown.sql、その他の場合はネイティブの取り消しコマンド)を使ってテストします。 -
制約は最初の防衛線です。
NOT NULL、UNIQUE、FOREIGN KEY、CHECK制約は、アプリケーションコードにかかわらず、データベースで不正なデータを阻止します。それぞれの制約が存在することと、適切なエラーで不正なデータを拒否することをテストしてください。 -
決定的なシードデータを使います。 テストの結果は毎回同じでなければなりません。ランダムなデータではなく、固定 ID と固定タイムスタンプを持つファクトリーを使用してください。シードデータでシードを指定しない
faker.random()、uuid()、now()を使うと、テストが非決定的になります。 -
テストごとにデータベースの状態を分離します。 状態を共有するテストは、実行順序に依存し、不安定になります。トランザクションのロールバック、テストごとのデータベース、または確実なクリーンアップを使用してください。
-
ORM の同期ではなく、マイグレーションをテストします。
prisma db pushやtypeorm synchronize: trueは、ユーザーが実際に実行するマイグレーションの経路を通りません。必ず実際のマイグレーションファイルを使用してください。 -
失敗しないパフォーマンスアサーションに価値はありません。
EXPLAINテストが成功した状態を信頼する前に、インデックスを削除すると失敗することを確認してください。「検証」を参照してください。
マイグレーションテスト
実行可能なマイグレーションテストのコードについては、references/migration-tests.md を参照してください。
前進マイグレーションの検証
新しく空のデータベースを起動し、prisma migrate deploy ですべてのマイグレーションを実行したうえで、information_schema を調べ、期待されるテーブルと列が適切な型、NULL 許容性、デフォルト値で存在することをアサートします。Testcontainers が提供する DATABASE_URL を優先してください。管理用の Pool から CREATE DATABASE を実行する方法は、稼働中の Postgres を対象にする必要がある場合の代替手段です。スイートごとにどちらか一方の戦略を選び、混在させないでください。
ロールバックテスト
Prisma に migrate down/migrate rollback コマンドはありません。prisma migrate resolve --rolled-back はロールバック用ツールではありません。migrate deploy が失敗したマイグレーションを修正するだけで、正常に適用済みのものに対してはエラーになります。可逆な変更をテストするためにサポートされている方法は、順方向に適用して状態を記録し、手書きの down.sql を直接実行(psql -f down.sql)し、元に戻したオブジェクトがなくなったことを確認してから、再適用することです。各マイグレーションディレクトリに down.sql を用意してください。
TypeORM と Sequelize には、どちらも組み込みの取り消しコマンド(dataSource.undoLastMigration()、sequelize-cli db:migrate:undo)があります。psql -f down.sql の手順をこれらに置き換えてください。状態の記録 → 取り消し → 検証 → 再適用という流れは同じです。
Drizzle Kit v1.0(2026 年半ば時点でまだベータ版です。最新版は [email protected] で、安定版 GA はまだありません。安定性が必要なら保守的なバージョン固定として 0.44.x を使用してください):drizzle-kit generate と drizzle-kit migrate を使用します。CI では正確なバージョンを固定してください。v1 ベータ系列では casing API が刷新され、Postgres の RQB v1 ._query が削除されており、API はベータ版ごとにまだ変更されています。Drizzle にはダウンマイグレーションのジェネレーターがないため、Prisma と同様に独自の逆 SQL をリポジトリに追加してください。
マイグレーション中のデータ保持
追加したカラムが既存の行を保持することをテストするには、N-1 までマイグレーションを適用してデータを挿入し、テスト対象のマイグレーションを適用して、行が保持されていることを確認します(新しい NULL 許容カラムにはデフォルト値または null が入ります)。prisma migrate deploy に --to フラグはありません。保留中のマイグレーションをすべて適用します。N-1 で止めるには、N-1 までのマイグレーションのみを含むディレクトリを用意して(CI でステージングして)、そのディレクトリをデプロイした後に、完全なディレクトリをデプロイしてください。Flyway の -target= や Alembic の upgrade <rev> のように、実際に対象を指定できるツールでは、代わりに組み込みフラグを使用します。
マイグレーションのドリフト検出
実際によく起きるマイグレーションのバグは、誰かが対応するマイグレーションなしで DB またはスキーマを編集し、コミット済みのマイグレーションでは schema.prisma を再現できなくなることです。prisma migrate diff --from-migrations … --to-schema-datamodel … --exit-code はドリフトがあると 0 以外を返します。より時間のかかるテストの前に行う迅速な事前チェックとして、これを CI に組み込んでください。references/migration-tests.md を参照してください。
スキーマスナップショットの比較
マイグレーションの前後で pg_dump --schema-only のスナップショットを取得し、意図したテーブルだけが変更されたことを確認できるよう、テーブルごとに差分を比較します。references/migration-tests.md を参照してください。
その他の ORM
TypeORM: migrationsRun: false を指定した DataSource を使用し、テスト内で dataSource.runMigrations() と dataSource.undoLastMigration() を実行します。すべてを適用し、スキーマを検証し、最後のマイグレーションを取り消して、ロールバックを検証するという同じ流れです。
Alembic(Python): 空の DB からの alembic upgrade head、完全にロールバックする alembic downgrade base、およびスキーマの一貫性を検証するアップグレード → ダウングレード → アップグレードのサイクルをテストします。フィクスチャを使って新しいテストデータベースを用意してください。
データ整合性テスト
実行可能な制約と参照整合性のコードについては、references/integrity-and-seed.md を参照してください。
制約テスト
各制約が不正なデータを拒否することを検証します。NOT NULL は必須カラムの欠落を、UNIQUE は重複を、FOREIGN KEY は参照先のない値を、CHECK は範囲外の値を拒否し、ON DELETE CASCADE は従属する行を削除します。ORM やアプリケーションのバリデーション層ではなく、pool.query のレベルでデータベースのエラーメッセージ(/null value in column/、/unique constraint/i など)を検証してください。ORM やアプリケーションのバリデーション層では、DB 制約の欠落が見過ごされることがあります。
参照整合性とデータ品質の監査
アンチ結合クエリ(LEFT JOIN … WHERE parent.id IS NULL)を実行して、削除済みの親を参照する孤立レコードがないことを検証します。次に、意図されている整合性と強制されている整合性のギャップを監査します。COUNT(*) と COUNT(DISTINCT col) の差から、一意であるべきなのに制約がないカラムを特定でき、COUNT(*) FILTER (WHERE col IS NULL) から、非 NULL であるべきカラムを特定できます。references/integrity-and-seed.md を参照してください。
データ型の検証
テスト内容:金額が正しい精度で保存されること(浮動小数点による損失がないこと)、VARCHAR の長さが制限されること(長さを超えると value too long になること)、タイムゾーン付きのタイムスタンプが UTC として保存されることを確認します。オフセット(+02:00)を指定して挿入し、取得して、ISO 形式の UTC 出力を検証します。
シードデータの管理
実行可能なファクトリー、シードスクリプト、分離のコードについては、references/integrity-and-seed.md を参照してください。
ファクトリーパターン(TypeScript)
カウンターをインクリメントして安定した決定論的な ID とメールアドレスを生成し、固定のタイムスタンプ(new Date('2026-01-01T00:00:00Z')。引数なしの new Date() は使用しません)を使う buildUser(overrides) ファクトリーからレコードを作成します。挿入してレコードを返す createUser(pool, overrides) ヘルパーも用意します。references/integrity-and-seed.md を参照してください。
Prisma シードスクリプト
固定 ID を指定して upsert を使い、シードを冪等かつ再実行可能にします。また、process.env.SEED_ENV でプロファイルを切り替えます。test(最小限、ユーザー 2~3 人)、staging(現実的な規模、ユーザー 50 人以上)、demo(厳選したデータ)を用意します。staging と demo は test を拡張します。references/integrity-and-seed.md を参照してください。
トランザクションのロールバックによるテストの分離
各テストを BEGIN/ROLLBACK で囲み、テスト間で挿入データが残らないようにします。モジュールレベルの共有クライアントはシリアル実行(jest --runInBand)で機能します。同一ワーカー内の並列テストファイルでは、スイートごとのクライアントまたはセーブポイントが必要です。references/integrity-and-seed.md を参照してください。
クエリパフォーマンスのテスト
実行可能な EXPLAIN ANALYZE とインデックス検証のコードについては、references/performance-and-docker.md を参照してください。
EXPLAIN ANALYZE のパターン
重要なクエリに対して EXPLAIN (ANALYZE, FORMAT JSON) を実行し、plan.Plan['Node Type'] を読み取って、/Index/(Seq Scan ではない)に一致することと、plan['Execution Time'] がしきい値未満であることをアサートします。references/performance-and-docker.md を参照してください。
インデックスの検証
pg_indexes に問い合わせ、参照や範囲スキャンに依存するカラム(users.email、orders.user_id、orders.created_at)に実際にインデックスが設定されていることをアサートします。references/performance-and-docker.md を参照してください。
遅いクエリの検出
現実的な規模のデータ(1 万行以上)をシードし、performance.now() で実行時間を測定します。重要なクエリ(JOIN、GROUP BY、ORDER BY を含むダッシュボードの集計)がしきい値(例:100ms)以内に完了することをアサートします。
MongoDB: collection.find(...).explain('executionStats') を使ってインデックスが使用されていることを確認します(stage が COLLSCAN ではないこと)。totalDocsExamined が nReturned に近いことを確認し、collection.indexes() で複合インデックスの存在を検証します。
Docker ベースのテストデータベース
推奨(2026):Testcontainers。 @testcontainers/postgresql 11.14 以降(2026 年 5 月)は、手間の少ないデフォルトの選択肢です。コンテナーのライフサイクルをプログラムで管理し、自動クリーンアップを行い、個別のポートを使って並列実行できます。docker-compose ファイルとポート競合を管理する手間がなくなります。PostgreSqlContainer の設定については、references/performance-and-docker.md を参照してください。
手動で作成した compose(引き続き有効): postgres:18-alpine、RAM ベースのストレージ用の tmpfs、および pg_isready ヘルスチェックを使った docker-compose.test.yml を用意します。ローカルの Postgres との競合を避けるため、デフォルト以外のポート(例:5433)にマッピングします。本番環境とメジャーバージョンを揃えてください。現行は Postgres 18(18.4、2026 年 5 月)です。本番環境のバージョンが固定されている場合を除き、17 から更新してください。
package.json でスクリプトを連結します。test:db:up(compose up)、test:db:migrate(prisma migrate deploy)、test:db:seed(prisma db seed)、test:db(すべての処理と jest)、test:db:down(compose down -v)を用意します。
アンチパターン
1. 本番データベースのコピーを使ったテスト
本番データには個人を特定できる情報が含まれ、決定論的ではなく、予測できない形で変化します。ファクトリーとシードスクリプトで合成データを使ってください。
2. テスト間でデータベースの状態を共有する
テスト A がユーザーを挿入し、テスト B はそのユーザーが存在すると想定します。CI が実行順序を入れ替えると、テスト B は失敗します。トランザクションのロールバックまたはテストごとのクリーンアップを使ってください。
3. ロールバックテストを省略する
「マイグレーションをロールバックすることはありません」という考えは、最初のマイグレーションで本番環境に障害が発生するまでしか通用しません。down パスをテストしてください。ツールに取り消し機能がない場合は、スキップするのではなく、リスクとして記録してください。
4. migrate resolve --rolled-back でロールバックを偽装する
このコマンドは 失敗した マイグレーションを修復するだけで、正常なマイグレーションに対してはエラーになります。スキーマをロールバックするものではありません。実際の仕組みを使って戻してください。Prisma なら down.sql、TypeORM なら undoLastMigration() を使います。
5. マイグレーションの代わりに ORM の同期を使う
prisma db push、typeorm synchronize: true、Django の migrate --run-syncdb は、実際のマイグレーションパスをスキップします。テストでは、本番環境と同じ仕組みを使う必要があります。
6. 成功パターンのクエリしかテストしない
データが存在するときに行を返すクエリは、簡単なケースです。結果が空の場合、オプション列が null の場合、結果の最大件数、誤ったデータに対するクエリをテストしてください。
7. 決して失敗しないパフォーマンスアサーション
インデックスの有無にかかわらず成功する EXPLAIN テストでは、誤った安心感が生まれます。インデックスを削除するとテストが失敗することを確認してください(「検証」を参照)。
8. ランダムなデータでシードする
固定シードなしの faker.random()、uuid()、now() は実行のたびに異なるデータを生成するため、テストが非決定的になります。固定シードと固定値を使ってください。faker.seed(42)、明示的な ID、new Date('2026-01-01T00:00:00Z') などを使用します。
検証
テストスイートが実際にリグレッションを検出できることを、小さなチェックから順に確認します。
- クリーンな状態からテストスイートが成功する: 新しい Testcontainers データベースに対して
npm run test:dbを実行し、終了コード 0 が返ることを確認します。 - EXPLAIN テストが確実に失敗する: 一時的な DB で
DROP INDEX users_email_idxを実行し、クエリパフォーマンステストを再実行します。プランナーがSeq Scanにフォールバックしてテストが失敗することを確認し、その後インデックスを復元してテストが再び成功することを確認します。インデックスを削除しても成功するパフォーマンステストは壊れています。信頼する前に修正してください。references/performance-and-docker.mdを参照してください。 - ドリフトチェックが検出する:
prisma migrate diff --from-migrations prisma/migrations --to-schema-datamodel prisma/schema.prisma --exit-codeは、クリーンなリポジトリでは 0 を返します。schema.prismaを手動で編集し、0 以外が返ることを確認してください。
完了条件
- 最新のマイグレーションについて、前進とロールバックのテストファイルが存在します。前進テストでは空の DB に適用し、
information_schemaを使ってスキーマをアサートします。ロールバックテストではdown.sqlを適用して、ロールバックされたオブジェクトが存在しないことをアサートした後、再適用します。これらのテストが CI で成功します。 - 制約テストでは、
pool.queryのレベルで NOT NULL、UNIQUE、FOREIGN KEY、CHECK のそれぞれについて、DB のエラーメッセージとともに拒否されることをアサートします。 - データ保持テストでは、テスト対象のマイグレーションより前に行を挿入し、それらがマイグレーション後も保持されることをアサートします(偽の
--toフラグは使いません)。 - マイグレーションのドリフトチェック(
prisma migrate diff … --exit-code)が CI で実行され、クリーンなリポジトリで終了コード 0 を返します。 - シードデータは冪等(
upsertと固定 ID を使用)で、SEED_ENVによってプロファイルを切り替えます。2 回再実行しても同一の状態になります。 - EXPLAIN テストでは、
Node Typeが/Index/に一致し、Execution Timeがしきい値未満であることをアサートします。また、インデックスを削除すると失敗することが確認されています。 test:dbCI ジョブが Testcontainers データベースに対して終了コード 0(成功)を返します。
参考ファイル(references/ 内)
- migration-tests.md — 前進検証、
down.sqlによるロールバック、データ保持、ドリフト検出(migrate diff)、スキーマスナップショットの比較について説明します。 - integrity-and-seed.md — 制約テストと参照整合性テスト、データ品質監査、ファクトリーパターン、
SEED_ENVを使ったシードスクリプト、トランザクションロールバック分離ヘルパーについて説明します。 - performance-and-docker.md — EXPLAIN ANALYZE のプランアサーション、インデックス検証、インデックス削除による検証テスト、Testcontainers のセットアップについて説明します。
関連するスキル
- test-data-management — 非本番環境向けの大規模な合成データ生成とマスキングを行います。テスト内のファクトリーやシードスクリプトについてはこのスキルを、現実的な大規模データセットや PII のマスキングについてはそちらを参照してください。
- test-environments — Docker/IaC によるテストデータベースのプロビジョニングや、環境間の同等性を扱います。このスキルではテストスイート内で Testcontainers を使用します。常設インフラストラクチャは test-environments が担当します。
- security-testing — SQL インジェクション、データベースレベルのアクセス制御、暗号化の検証を扱います。このスキルでは整合性と正確性をテストしますが、敵対的な入力は対象外です。
- ci-cd-integration — CI パイプラインでのマイグレーションおよび DB テストジョブの実行と、GitHub Actions でのテストデータベースのプロビジョニングを扱います。
- performance-testing — DB のパフォーマンス、コネクションプールのサイズ設定、同時負荷下でのクエリ最適化の負荷テストを扱います(ここでは対象外です)。