📝

テストのために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.yamlhealthcheckを追加します。

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を使うことで簡単に解決できます。
ぜひ参考にしてみてください。

mutex Tech Blog

Discussion