qe-database-testing について
qe-database-testing は、スキーマ、データ整合性、マイグレーション、トランザクション、クエリ性能を検証するテストスキルです。開発者やテスター向けに、可逆なマイグレーションと並行トランザクションの確認方法を示します。
主な機能
- 01テーブル、列、制約の検証
- 02一意性、外部キー、データ整合性の確認
- 03マイグレーション実行とロールバックのテスト
- 04トランザクション分離と ACID 特性の検証
- 05クエリ性能とインデックス利用の測定
活用シーン
- 01新規テーブルやデータベーススキーマの検証
- 02データを保持してマイグレーションを戻せるか確認
- 03エラー発生時のトランザクションロールバックをテスト
- 04クエリ性能の問題を調査
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ proffesor-for-testing/ agentic-qe --skill qe-database-testingインストール後、Claude Code で/qe-database-testingを入力して読み込まれたか確認します。
詳細
データベーステスト
<default_to_action> データベース操作をテストする場合:
- スキーマを検証します(テーブル、列、制約が想定どおり存在すること)
- データの整合性をテストします(一意制約、外部キー制約、CHECK 制約)
- マイグレーションを確認します(適用が成功すること、ロールバックが成功すること、データが保持されること)
- トランザクションの分離性をチェックします(ACID 特性、並行アクセス)
- クエリのパフォーマンスを測定します(インデックスが使用されていること、実行時間)
データベーステストの簡易チェックリスト:
- スキーマが仕様と一致している
- 一意制約によって重複が防止される
- 外部キー制約によって孤立レコードが防止される
- マイグレーションを元に戻せる
- エラー時にトランザクションがロールバックされる
成功のための重要な要因:
- データベースのバグはデータの損失/破損を引き起こします(致命的です)
- 本番環境の前にステージング環境でマイグレーションをテストします
- トランザクションテストで並行処理のバグを検出できます </default_to_action>
クイックリファレンスカード
使用する場面
- 新しいテーブル/スキーマの作成
- マイグレーションの開発
- データ整合性の検証
- クエリパフォーマンスの最適化
データベーステストの種類
| 種類 | 対象 | タイミング |
|---|---|---|
| スキーマ | 構造が正しいこと | テーブル作成時 |
| 整合性 | 制約が機能すること | データ操作時 |
| マイグレーション | 適用/ロールバックが機能すること | スキーマ変更時 |
| トランザクション | ACID 特性 | 並行アクセス時 |
| パフォーマンス | クエリ速度 | 最適化時 |
ACID 特性
| 特性 | テスト対象 |
|---|---|
| 原子性 | すべて成功するか、すべて失敗すること(エラー時にロールバック) |
| 一貫性 | 制約が常に有効であること |
| 分離性 | 並行するトランザクションが互いに干渉しないこと |
| 永続性 | コミット済みのデータが保持されること |
スキーマテスト
test('users table has correct schema', async () => {
const schema = await db.raw(`
SELECT column_name, data_type, is_nullable
FROM information_schema.columns
WHERE table_name = 'users'
`);
expect(schema).toContainEqual({
column_name: 'id',
data_type: 'integer',
is_nullable: 'NO'
});
expect(schema).toContainEqual({
column_name: 'email',
data_type: 'character varying',
is_nullable: 'NO'
});
});
データ整合性テスト
test('email must be unique', async () => {
await db.users.create({ email: '[email protected]' });
await expect(
db.users.create({ email: '[email protected]' })
).rejects.toThrow('unique constraint violation');
});
test('foreign key prevents orphaned records', async () => {
const user = await db.users.create({ email: '[email protected]' });
await db.orders.create({ userId: user.id, total: 100 });
await expect(
db.users.delete({ id: user.id })
).rejects.toThrow('foreign key constraint');
});
マイグレーションテスト
test('migration is reversible', async () => {
await migrate('add-users-table');
// Table exists after migration
const tables = await db.raw(`SELECT table_name FROM information_schema.tables`);
expect(tables.map(t => t.table_name)).toContain('users');
await rollback('add-users-table');
// Table gone after rollback
const tablesAfter = await db.raw(`SELECT table_name FROM information_schema.tables`);
expect(tablesAfter.map(t => t.table_name)).not.toContain('users');
});
test('migration preserves existing data', async () => {
await db.users.create({ email: '[email protected]' });
await migrate('add-age-column');
const user = await db.users.findOne({ email: '[email protected]' });
expect(user).toBeDefined();
expect(user.age).toBeNull(); // New column, null default
});
トランザクションテスト
test('transaction rolls back on error', async () => {
const initialCount = await db.users.count();
try {
await db.transaction(async (trx) => {
await trx('users').insert({ email: '[email protected]' });
await trx('users').insert({ email: '[email protected]' });
throw new Error('Rollback test');
});
} catch (error) { /* Expected */ }
expect(await db.users.count()).toBe(initialCount);
});
test('concurrent transactions isolated', async () => {
const user = await db.users.create({ balance: 100 });
// Two concurrent withdrawals (race condition test)
await Promise.all([
db.transaction(async (trx) => {
const current = await trx('users').where({ id: user.id }).first();
await trx('users').update({ balance: current.balance - 50 });
}),
db.transaction(async (trx) => {
const current = await trx('users').where({ id: user.id }).first();
await trx('users').update({ balance: current.balance - 50 });
})
]);
const final = await db.users.findOne({ id: user.id });
expect(final.balance).toBe(0); // Proper isolation
});
エージェント主導のデータベーステスト
// Generate test data with integrity
await Task("Generate Test Data", {
schema: 'ecommerce',
tables: ['users', 'products', 'orders'],
count: { users: 1000, products: 500, orders: 5000 },
preserveReferentialIntegrity: true
}, "qe-test-data-architect");
// Test migration safety
await Task("Migration Test", {
migration: 'add-payment-status-column',
tests: ['forward', 'rollback', 'data-preservation'],
environment: 'staging'
}, "qe-test-executor");
エージェント連携のヒント
メモリ名前空間
aqe/database-testing/
├── schema-snapshots/* - Current schema state
├── migrations/* - Migration test results
├── integrity/* - Constraint validation
└── performance/* - Query benchmarks
フリート連携
const dbFleet = await FleetManager.coordinate({
strategy: 'database-testing',
agents: [
'qe-test-data-architect', // Generate test data
'qe-test-executor', // Run DB tests
'qe-performance-tester' // Query performance
],
topology: 'sequential'
});
関連スキル
- test-data-management - テストデータを生成します
- performance-testing - クエリパフォーマンス
- compliance-testing - データ保護
忘れないでください
データベースのバグは致命的です。 データの損失は復旧できません。破損は気付かれないまま広がります。パフォーマンスの問題は積み重なります。
本番環境の前にマイグレーションをテストします: 適用が成功すること、ロールバックが成功すること、データが保持されること、パフォーマンスが許容範囲であることを確認します。テストしていないマイグレーションをデプロイしてはいけません。
エージェントを使用する場合: qe-test-data-architect は参照整合性を保ちながら、現実的なテストデータを生成します。qe-test-executor は CI/CD でマイグレーションを自動的に検証します。
コメント
Markdown 対応