Node.js と Mocha フレームワークを用いたテスト実行
読む時間の目安: 6 分
前提条件
開発向けコンテナーの利用 において、イメージビルドを行いコンテナー化アプリケーションを実行していること。
はじめに
最近のソフトウェア開発においてテストは重要な工程です。
テストは、さまざまな開発チームによって実に多くのことを実施します。
ユニットテスト、結合テスト、総合テスト(end-to-end testing)です。
本ガイドでは Docker においてユニットテストを実行していきます。
前提として、アプリケーション内の./test
フォルダーには Mocha テストが定義されているものとします。
テストの生成
そこでアプリケーション内の./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 コマンドを用いますが、ただし今回の場合、コンテナー内部にある CMD をオーバーライドして npm run test を実行するようにします。 このコマンドは、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
というフラグをつけます。
これによってテストビルドステージを指定してビルドします。
$ 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 test runner の実行は完了し、テストが正常終了していることがわかります。
うまくできましたが、これでもイメージのビルドとテスト実行に対して 2 つの docker コマンドを実行しなければなりません。 これを少しだけ改善します。 テストステージにおいて CMD 命令ではなく RUN 命令を使うようにします。 CMD 命令はイメージビルド時には実行されません。 これが実行されるのはコンテナー内においてイメージが実行されたときです。 一方 RUN 命令の場合は、イメージビルドの最中にテストが実行され、テストが失敗したときにはビルドが停止します。
以下の 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 . .
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
ここでもわかりやすくなるように、ビルド結果の出力は省略しましたが、テストの実行と正常終了が見てとれます。 そこでテストが失敗したときにはどうなるかを見るために、ブレークポイントを設定して出力結果を確認します。
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 アクションを使って CI/CD パイプラインを設定する方法を見ていきます。 以下を参照してください。
フィードバック
本トピック改善のためにフィードバックをお寄せください。 お気づきの点があれば Docker Docs の GitHub リポジトリに issue をあげてください。 あるいは PR の生成 により変更の提案を行ってください。