📝
テストのためにDockerでDBを立てるときのTips
はじめに
CIなどでDBを使用したテストを行う際に、Dockerを使ってDBを立てることが多いと思います。
今回は、DBを立ててテストを実行するCLIを叩く際の簡単なTipsを紹介します。
問題
以下のようなcompose.yaml
を用意して、DBを立てます。
compose.yaml
name: db-for-test
services:
postgres:
image: postgres:17
ports:
- 127.0.0.1:3000:5432
environment:
POSTGRES_HOST_AUTH_METHOD: ${POSTGRES_HOST_AUTH_METHOD:-trust}
POSTGRES_USER: ${POSTGRES_USER:-postgres}
volumes:
- ./postgres-entrypoint-initdb.d
networks:
- default
networks:
default:
このcompose.yaml
を使ってDB操作を伴うテストを実行すると、以下のようなエラーを吐きます。(今回はNode.jsのテストを実行する前提で、vitest
を使用しています)
docker compose up -d && npx vitest run
[+] Running 2/2
✔ Network db-for-test_default Created 0.0s
✔ Container db-for-test-postgres-1 Started 0.0s
RUN v3.1.1 /Users/username/workspace
...
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯ Unhandled Error ⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯
...
Error: Connection terminated unexpectedly
at /Users/username/workspace/node_modules/pg-pool/index.js:45:11
at process.processTicksAndRejections (node:internal/process/task_queues:105:5)
at async PgDialect.migrate (/Users/username/workspace/node_modules/src/pg-core/dialect.ts:85:3)
at async migrate (/Users/username/workspace/node_modules/src/node-postgres/migrator.ts:10:2)
これは、docker compose up
でDBを立てた後に、DBが立ち上がる前にテストを実行してしまっているためです。
解決策
この問題を解決するにはDBが立ち上がるまで待つ必要がありますが、CLIで数秒待つなどの処理をするのは少しナンセンスです。
そこで、Docker Composeのhealthcheck
を使って、DBが立ち上がるまで待つようにします。
以下のように、compose.yaml
にhealthcheck
を追加します。
compose.yaml
name: db-for-test
services:
postgres:
image: postgres:17
ports:
- 127.0.0.1:3000:5432
environment:
POSTGRES_HOST_AUTH_METHOD: ${POSTGRES_HOST_AUTH_METHOD:-trust}
POSTGRES_USER: ${POSTGRES_USER:-postgres}
volumes:
- ./postgres-init:/docker-entrypoint-initdb.d
networks:
- default
healthcheck:
test: ['CMD-SHELL', 'pg_isready -U postgres']
interval: 5s
timeout: 5s
retries: 5
wait-for-postgres:
image: busybox
depends_on:
postgres:
condition: service_healthy
command: ['echo', 'Postgres is ready!']
networks:
default:
wait-for-postgres
というサービスを追加し、postgres
サービスが立ち上がったら、wait-for-postgres
サービスが起動するようにします。
このサービスは、busybox
という非常に軽いイメージを使って、Postgresが立ち上がったらecho
コマンドを実行するだけのサービスです。
docker compose up -d && npx vitest run
[+] Running 3/3
✔ Network db-for-test_default Created 0.0s
✔ Container db-for-test-postgres-1 Healthy 5.8s
✔ Container db-for-test-wait-for-postgres-1 Started 5.9s
RUN v3.1.1 /Users/username/workspace
...
このように、docker compose up -d
を実行すると、wait-for-postgres
サービスが起動するまで待ってくれるようになります。
まとめ
知らないと少し煩わしい問題ですが、healthcheck
を使うことで簡単に解決できます。
ぜひ参考にしてみてください。
Discussion