PostgreSQLテンプレートDBでAPIテストを高速化した話と、GitHub Copilot Coding Agentの意外な弱点
はじめに
こんにちは。GVA TECH株式会社でエンジニアをしているaxia(あしゃ)です。
この記事では主に、PostgreSQLのテンプレートDB機能を活用してテストの実行時間を短縮した取り組みについて紹介します。
またその過程で、GitHub Copilot Coding Agentを使って大量のテストコードを一括でリファクタしようとしたところ、月毎のクオータを使い潰すという課題にも遭遇しました。
これについても、現時点での所感を共有したいと思います。
同じようにテスト高速化に取り組んでいる方や、これから新しいプロダクトのプロトタイプを構築する方の参考になれば幸いです。
技術スタック
- TypeScript
- NestJS
- Prisma
- PostgreSQL
- Jest
取り組みの背景
これまで各テストケースの前処理としてRDBの全テーブルに対してTRUNCATEクエリを実行し、seedデータを再投入するというステップを踏んでおり、一つのテストケースの完了に5秒前後かかっていました。
テストの数が増えていくにつれてCIの実行時間も増大してきましたが、CIのジョブを分割して並列実行する等の対策によりクリティカルな問題を回避してきました。
とはいえ、開発過程ではローカルでも実行しますし、根本的な部分でseedの初期化時間を短縮し、コンピューティングリソースを節約することが今後も成長を続けるプロダクトの開発生産性に寄与すると考えました。
取り組みの効果
1テストケース辺りの実行時間は80%減少し、CI全体の待ち時間は50%短縮されました。
1テストケース:約5秒 → 500~800msec(内、DBの初期化は300msec前後)
CI全体:約20分 → 約10分
テンプレートDBを利用したテストデータの初期化手順
具体的には以下のステップでテストの初期化を行います。
ステップ(1)はテスト開始時のみ、ステップ(2)(3)を関数化してbeforeEachで実行します。
-
prisma migrate reset
により、テスト対象となるDBを構築しseedも投入する - (1)をテンプレートとして新しいDBにバックアップする
- 各テストの前処理で(1)を削除し、(2)をテンプレートにして(1)を再作成する
テンプレートを指定してDBを作成する
CREATE DATABASE test_db TEMPLATE test_template;
※ステップ(2)では作成対象とテンプレートが入れ替わります
DBを削除する
コネクションの切断
DBを削除するには予め全てのコネクションを切断する必要があります。
SELECT pg_terminate_backend(pg_stat_activity.pid)
FROM pg_stat_activity
WHERE pg_stat_activity.datname = 'test_db'
AND pid <> pg_backend_pid()
DB削除
DROP DATABASE IF EXISTS test_db
コネクションの強制切断に伴う注意点
前述の通り、beforeEach内でテスト対象のDBに対するコネクションを強制切断してDBを再作成した場合、Nestアプリケーションを再生成する必要があります。
これはテスト対象のAPIにDIされているPrismaインスタンスのコネクションが切断されてしまう為で、各テストケースの前にアプリケーションを初期化し、Prismaインスタンスが再生成されるようにします。
本来、Nestアプリケーションの生成はテストケース毎に行うべきで、仮にbeforeAll等で1回だけ行う構成にすると、APIにDIされているインスタンスに前回のテストの影響が残ってしまいます。
修正例.
const initApp = async ()=>{
const moduleRef = await Test.createTestingModule({
imports: [AppModule],
}).compile()
...中略
app = moduleRef.createNestApplication()
await app.init()
}
beforeEach(async ()=>{
initDb() // DBの再作成(コネクション切断)
initApp() // アプリケーションの初期化(Prismaインスタンスが再生成されコネクション復活)
})
終わりに:GitHub Copilot Coding Agentの弱点について
今回は各テストファイルのbeforeAll、beforeEachをリファクタする必要に迫られた為、Issueを作成し、Copilotを使ってリファクタ作業を進めようと試みました。
Issueには修正対象となるファイルの条件と修正方針を具体的に記載して指示しました。
修正対象ファイルが400ファイル以上存在していましたが、Copilotはまず数ファイルの修正を行った段階で作業を止めました。(これ自体はある意味良かったです)
そして修正内容を確認し、修正方針を訂正する等したあと、残りの修正についても同様に進めるように、対象となる全てのファイルの修正が終わるまで繰り返すように指示したところ、ある程度繰り返してくれましたが、最終的に私に割り当てられていたCopilotの月間クオータを使い果たす結果となり、1回の指示で最後まで修正できたかどうかは確認できませんでした。
所感
GitHub Copilot Coding Agentについて、執筆時点(2025年7月)では以下の所感を持っています。
- 1回の指示で全てのファイルが修正しきれないことがある(プロンプトの工夫で解決可能かどうかは不明)
- 大量ファイルの修正を任せるのは実行コストが高い
Copilotの機能は今後もどんどん改善されていくと思いますので、近い将来今回の様なユースケースにも対応されることを期待しています。
Discussion