開発にコンテナを使う

事前準備

開発にコンテナを使う でイメージ構築をし、それコンテナ化アプリケーションとして実行します。

概要

現代的なソフトウェア開発において、テストは必要不可欠です。テストとは開発チームによって様々な意味を持ちます。単体テスト、統合テスト、エンドツーエンドテストがあります。このガイドとは Docker での単体テスト実行をみていきます。

テストの作成

アプリケーション内の ./test ディレクトリ内で Mocha テストを定義しましょう。

$ mkdir -p test

以下のコードを ./test/test.js に保存します。

var assert = require('assert');
describe('Array', function() {
  describe('#indexOf()', function() {
    it('should return -1 when the value is not present', function() {
      assert.equal([1, 2, 3].indexOf(4), -1);
    });
  });
});

ローカルで実行とアプリケーションのテスト

Docker イメージを構築し、全てが正しく動くのを確認しましょう。コンテナで Docker イメージを構築および実行するには、次のコマンドを実行します。

$ docker-compose -f docker-compose.dev.yml up --build

次は、JSON ペイロードを POST してアプリケーションをテストし、 HTTP GET リクエストを使って JSON が正しく保存されているのを確認します。

$ curl --request POST \
  --url http://localhost:8000/test \
  --header 'content-type: application/json' \
  --data '{"msg": "testing"}'

それから、同じエンドポイントに対して GET リクエストを処理し、保存された JSON ペイロードが正しく取得できるのを確認します。「id」と「createDate」は、みなさんのものとは違うでしょう。

$ curl http://localhost:8000/test

{"code":"success","payload":[{"msg":"testing","id":"e88acedb-203d-4a7d-8269-1df6c1377512","createDate":"2020-10-11T23:21:16.378Z"}]}

Mocha のインストール

Mocha をインストールするため以下のコマンドを実行し、これを開発者の依存関係に追加します:

$ npm install --save-dev mocha

テストを実行するため package.json と Dockerfile を更新

それでは、次はアプリケーションが正しく動作しているかどうかを知るため、コンテナ内でテストを実行してみましょう。先ほどと同じ docker run コマンドを使いますが、今回はコンテナ内で npm run test を実行するため CMD を上書きします。これにより、 package.json ファイルの「script」セクションにあるコマンドが実行されます。以下をご覧ください。

{
...
  "scripts": {
    "test": "mocha ./**/*.js",
    "start": "nodemon --inspect=0.0.0.0:9229 server.js"
  },
...
}

以下の Docker コマンドはコンテナを起動し、テストを実行します:

$ docker-compose -f docker-compose.dev.yml run notes npm run test
Creating node-docker_notes_run ...

> node-docker@1.0.0 test /code
> mocha ./**/*.js



  Array
    #indexOf()
      ✓ should return -1 when the value is not present


  1 passing (11ms)

テスト用のマルチステージ Dockerfile

コマンドにテスト実行を追加するだけでなく、マルチステージ Dockerfile を使い、イメージ構築時にもテストを実行できます。以下の Dockerfile はテストを実行し、プロダクションのイメージを構築します。

# syntax=docker/dockerfile:1
FROM node:14.15.4 as base

WORKDIR /code

COPY package.json package.json
COPY package-lock.json package-lock.json

FROM base as test
RUN npm ci
COPY . .
CMD [ "npm", "run", "test" ]

FROM base as prod
RUN npm ci --production
COPY . .
CMD [ "node", "server.js" ]

まず、 FROM node:14.15.4 命令に as base ラベルを追加します。これにより、この名前で以降の構築ステージから参照できるようになります。次は、 test とラベルが付けられた新しいイメージを追加します。テストを実行するために、このステージを使います。

それでは、イメージを再構築し、テストを実行しましょう。先述と同じ docker build コマンドを実行しますが、今回は --target test フラグを付けますので、test 構築ステージのみ処理します。

$ docker build -t node-docker --target test .
[+] Building 66.5s (12/12) FINISHED
 => [internal] load build definition from Dockerfile                                                                                                                 0.0s
 => => transferring dockerfile: 662B                                                                                                                                 0.0s
 => [internal] load .dockerignore
 ...
  => [internal] load build context                                                                                                                                    4.2s
 => => transferring context: 9.00MB                                                                                                                                  4.1s
 => [base 2/4] WORKDIR /code                                                                                                                                         0.2s
 => [base 3/4] COPY package.json package.json                                                                                                                        0.0s
 => [base 4/4] COPY package-lock.json package-lock.json                                                                                                              0.0s
 => [test 1/2] RUN npm ci                                                                                                                                            6.5s
 => [test 2/2] COPY . .

これでイメージが構築されましたので、コンテナを実行できるようになり、テストに合格したかどうか分かります。

$ docker run -it --rm -p 8000:8000 node-docker

> node-docker@1.0.0 test /code
> mocha ./**/*.js



  Array
    #indexOf()
      ✓ should return -1 when the value is not present


  1 passing (12ms)

構築時の出力を省略しましたが、 Mocha テストランナーが完了し、全てのテストに合格したのが分かります。

現時点でこれは素晴らしいのですが、テストのために build と run という2つの docker コマンドを実行しました。test ステージ内の CMD 命令を RUN 命令に変えると若干改善できます。CMD 命令はイメージの構築中は実行されませんが、コンテナとしてイメージを実行する時に(CMD命令で指定したコマンドを)実行します。RUN 命令では、テストをイメージの構築中に実行し、失敗した場合は構築を停止します。

# syntax=docker/dockerfile:1
FROM node:14.15.4 as base

WORKDIR /code

COPY package.json package.json
COPY package-lock.json package-lock.json

FROM base as test
RUN npm ci
COPY . .
RUN npm run test

FROM base as prod
RUN npm ci --production
COPY . .
CMD [ "node", "server.js" ]

それでは、テストを実行するため、先ほどと同じ docker build コマンドを実行する必要があります。

$ docker build -t node-docker --target test .
[+] Building 8.9s (13/13) FINISHED
 => [internal] load build definition from Dockerfile      0.0s
 => => transferring dockerfile: 650B                      0.0s
 => [internal] load .dockerignore                         0.0s
 => => transferring context: 2B

> node-docker@1.0.0 test /code
> mocha ./**/*.js



  Array
    #indexOf()
      ✓ should return -1 when the value is not present


  1 passing (9ms)

Removing intermediate container beadc36b293a
 ---> 445b80e59acd
Successfully built 445b80e59acd
Successfully tagged node-docker:latest

シンプルにするため再び出力を一部省略しましたが、テストを実行して合格したのが分かります。テストの1つを失敗するようにし、テストが失敗するのを確認しましょう。

test/test.js ファイルを開き、5行目を以下のように変更します。

1  var assert = require('assert');
2  describe('Array', function() {
3    describe('#indexOf()', function() {
4      it('should return -1 when the value is not present', function() {
5        assert.equal([1, 2, 3].indexOf(3), -1);
6      });
7    });
8  });

これで、先ほどと同じ docker build コマンドを実行すると、構築が失敗し、コンソール上にはテスト失敗に関係する情報を表示するのが見えるでしょう。

$ docker build -t node-docker --target test .
Sending build context to Docker daemon  22.35MB
Step 1/8 : FROM node:14.15.4 as base
 ---> 995ff80c793e
...
Step 8/8 : RUN npm run test
 ---> Running in b96d114a336b

> node-docker@1.0.0 test /code
> mocha ./**/*.js



  Array
    #indexOf()
      1) should return -1 when the value is not present


  0 passing (12ms)
  1 failing

  1) Array
       #indexOf()
         should return -1 when the value is not present:

      AssertionError [ERR_ASSERTION]: 2 == -1
      + expected - actual

      -2
      +-1

      at Context.<anonymous> (test/test.js:5:14)
      at processImmediate (internal/timers.js:461:21)



npm ERR! code ELIFECYCLE
npm ERR! errno 1
npm ERR! node-docker@1.0.0 test: `mocha ./**/*.js`
npm ERR! Exit status 1
...

次のステップ

この章では、 Docker イメージの構築過程の一部としてテストを実行する方法を説明しました。

次の章では、GitHub Actions を使って CI/CD パイプラインのセットアップ方法を説明します。

CI/CD の設定

フィードバック

フィードバックを通し、このトピックの改善を支援ください。考えがあれば、 Docker Docs GitHub リポジトリに issue を作成して教えてください。あるいは、更新の提案のために RP を作成 してください。

参考

Run your Tests using Node.js and Mocha frameworks

https://docs.docker.com/language/nodejs/run-tests/