javascriptまたはEcmaScript6(ES6)のプログラムの品質を保つ方法を、備忘録としてまとめてみたいと思います。

今回は第二回目です。mocha、power-assertを使って、プログラム動作が期待通り書かれているのか検証を行う方法について分かりやすく解説していきたいと思います。

なお前回は、javascriptやES6におけるテスト環境についての概要や、テストの必要性について簡単に触れました。

私自身もテスト初心者ですので、できるだけ平たく説明していきますが、もしこれは違う!という内容があればツッコミをお待ちしております。

目次

javascriptで用いるモジュールをおさらい

前回も触れましたが、テストを行うために、以下のnpmパッケージを使います。

  • mocha (モカ)

    テストフレームワークと呼ばれているパッケージです。テストのプログラムを実行させることができる枠組みです。

  • power-assert (パワーアサート)

    テストフレームワークのmochaに、想定した動作ルールを書くことができ、想定外のプログラム動作が見つかるとエラーで教えてくれます。この動作ルールのことをアサーションと呼んでいます。

  • intelli-espower-loader (なんて読むんだろ?)

    power-assertを実行するために、コードを変換してくれるパッケージ。power-assertのテストを実行する際に内部で利用します。

今回は、上記のパッケージを使って、記述したjavascriptのプログラムが想定した動作を行っているのか?を調べてみたいと思います。

プロジェクトを作成し、パッケージのインストール

それでは、具体的に本体のプログラムや、テストプログラムを書いていきたいと思います。まずはjavascriptのプロジェクト(例:js-test-practiceディレクトリ)を作成し、そのディレクトリでnpmパッケージをインストールします。

bash
1
2
3
4
5
$ git clone https://github.com/tea3/js-test-practice.git
$ mkdir js-test-practice
$ cd js-test-practice
$ npm init
$ npm install --save-dev mocha power-assert intelli-espower-loader

npmでインストールするパッケージは前項で紹介したmocha、power-assert、intelli-espower-loaderです。

save-devオプションって何?

npm installコマンドの--save-devオプションは、開発で使うパッケージという意味合いを示すために指定しています。本体のプログラムでは使用せず、テストなど本体のプログラムで使わないパッケージがあれば、このオプションを指定してインストールしましょう。

また、node.jsやnpm、そしてコマンドラインについてご不明な場合は以下をご覧ください。

本体のプログラムを作成

続いて、プログラムの本体を書いて見たいと思います。

サンプルコード

サンプルコードはgithubにアップしていますので、そちらをご覧ください。

./index.jsを新規作成します。動作内容は下記のように引数へ名前を渡すと、挨拶の言葉を返すような関数を書いてみたいと思います。

./index.js
1
2
// node.js v8以降で動作します。
exports.hi = (name) => "やあ!"+ name

上記のhi(名前)というメソッドは、やあ!名前という結果を返す関数でなくてはなりません。今回はこの仕様をテストで検証してみたいと思います。

テストコードを作成

続いて、テストを書いてみたいと思います。テストは先程作成した./index.jsの動作が想定した仕様通りに動作するのかを検証できるように記述していきます。

それでは、./test/test.jsを新規作成し、次のようなコードを記述してみましょう。

./test/test.js
1
2
3
4
5
6
7
8
9
10
11
12
// node.js v8以降で動作します。
const assert = require('power-assert') // power-assertをインポート
const myModule = require('../index') // プログラム本体である、index.jsを読み込む

// テストコード
describe('作ったプログラムを次の項目ごとにテストします' , () => {
truedescribe('1. 挨拶のテスト その1', () => {
it('「やあ!」と挨拶しないとだめ', () => {
assert.equal(myModule.hi('太郎'), 'やあ!太郎')
})
})
})

テストでは、上記のようにdescribeというテストの説明が書ける関数の中で、いくつかのテストコードをit()の中で記述していきます。

テストコードの中を詳しく見ていきたいと思います。it()の中では、assert.equal()によって、index.jsのプログラムが想定した結果を返すのかが検証されています。上記の例では、myModule.hi('太郎')の結果が'やあ!太郎'となればテストは正常クリアされた事になります。

テストを実行してみる

それでは、上記で書いたmocha、power-assertのテストコードを実行して、本体プログラムのindex.jsが想定通りに動作しているのかチェックしてみましょう。

まず、$ npm initコマンドで作成されたpackage.jsonを開き、scriptsの項目を以下のように変更します。

./package.json
1
2
3
4
5
6
7
8
9
{
"name": "test-practice",
"version": "1.0.0",
"description": "javascript test practice",
"main": "index.js",
"scripts": {
"test": "mocha test/test.js --require intelli-espower-loader"
},
...

上記の記述によって、$ npm run testというコマンドから、テスト実行コマンドのmocha test/test.jsが実行できるようになります。また、テストコマンドでは、前半でご紹介したintelli-espower-loaderというパッケージを内部で使用する事によってテスト用のコードに変換してくれます。

bash
1
2
3
4
5
6
7
8
9
10
11
12
$ npm run test-normal

> mocha test/test.js



作ったプログラムを次の項目ごとにテストします
1. 挨拶のテスト その1
✓ 「やあ!」と挨拶しないとだめ


1 passing (1ms)

上記のように、1 passingと表示されれば、テストが期待した結果通りに動作したことを検証できた事になります。このように、テストコードで書いたdescribeの概要コメントが表示され、テストが通ったか否かを感覚的に把握できるのが、power-assertなどのアサーション・ライブラリと呼ばれるパッケージの特徴となっています。

テストに失敗すると、どうなるの?

前述のサンプルでは、テストに問題なくパスすることができました。しかし、テストに失敗するとどうるのでしょうか?試しに誤ったテストを書いてみましょう。

./test/test.js
1
2
3
4
5
6
7
8
9
10
11
12
// node.js v8以降で動作します。
const assert = require('power-assert') // power-assertをインポート
const myModule = require('../index') // プログラム本体である、index.jsを読み込む

// テストコード
describe('作ったプログラムを次の項目ごとにテストします' , () => {
truedescribe('1. 挨拶のテスト その1', () => {
it('「こんにちは!」と挨拶しないとだめ', () => {
assert.equal(myModule.hi('太郎'), 'こんにちは!太郎')
})
})
})

上記のテストコードでは、本体プログラムでは返さない結果「こんにちは!(名前)」を返すよう変更しました。それでは、テストコードを実行してみましょう。

bash
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
$ npm run test-normal

> mocha test/test.js


作ったプログラムを次の項目ごとにテストします
1. 挨拶のテスト その1
1) 「こんにちは!」と挨拶しないとだめ


0 passing (1ms)
1 failing

1) 作ったプログラムを次の項目ごとにテストします 1. 挨拶のテスト その1 「こんにちは!」と挨拶しないとだめ:

AssertionError [ERR_ASSERTION]: # test/test.js:30

assert.equal(myModule.hi('太郎'), 'こんにちは!太郎')
| |
| "やあ!太郎"
Object{hi:#function#,hello:#function#,helloSum:#function#}

+ expected - actual

-やあ!太郎
+こんにちは!太郎

上記のように、0 passing 1 failingと表示されると、テストは失敗という事になります。テストが失敗すると-やあ!太郎 +こんにちは!太郎のように、本体プログラムとテストコードで書いた想定値の食い違いを、直感的に把握することができます。

実際のテストでは、期待通りにプログラムが動作するようにテストを実行し、テストが失敗すればその箇所をデバッグするという流れになっていきます。

外部ファイルの読み込みもテストで検証できる

node.jsでは、外部ファイルに関数を分けて記述することも多々あるかと思います。テストでは、ファイルが幾つかに別れているケースも検証することができます。

それでは、試しに外部モジュール./lib/sample-lib.jsを作成し、以下のような関数を書いてみましょう。

./lib/sample-lib.js
1
2
3
4
5
6
7
8
9
10
// 挨拶を返す関数
exports.hello = (name) => "はろー!"+ name
// 合計を返す関数
exports.sum = (...arg) => {
let result = 0
for(let i of arg){
result += i
}
return result
}

続いて上記で作成した./lib/sample-lib.js./index.jsで読み込み、関数を使用します。

./index.js
1
2
3
4
5
6
7
8
// node.js v8以降で動作します。
const lib = require('./lib/sample-lib.js') // sample-lib.jsを読み込む

exports.hi = (name) => "やあ!"+ name

// 新たに追加するメソッド
exports.hello = (name) => lib.hello(name) // sample-lib.jsの関数を利用している
exports.helloSum = (name , ...arg) => `${lib.hello(name)}。合計は${lib.sum(...arg)}です。` // sample-lib.jsの関数を利用している

それでは、新たに追加したメソッドhello(name)helloSum(name, ...arg)の結果を検証するテストコードを2つ書いていきたいと思います。

./test/test.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// node.js v8以降で動作します。
const assert = require('power-assert') // power-assertをインポート
const myModule = require('../index') // プログラム本体である、index.jsを読み込む

// テストコード
describe('作ったプログラムを次の項目ごとにテストします' , () => {
truedescribe('1. 挨拶のテスト その1', () => {
it('「こんにちは!」と挨拶しないとだめ', () => {
assert.equal(myModule.hi('太郎'), 'こんにちは!太郎')
})
})

// 新たに追加するテストコード
describe('2. 挨拶のテスト その2', () => {
it('「はろー!」と挨拶しないとだめ', () => {
assert.equal(myModule.hello('太郎'), 'はろー!太郎')
})
})

// 新たに追加するテストコード
describe('3. 挨拶と合計を計算するテスト', () => {
it('挨拶と合計を計算しないとだめ', () => {
assert.equal(myModule.helloSum('太郎',1,2,3), 'はろー!太郎。合計は6です。')
})
})

})

上記で書いたテストを実行してみましょう。

bash
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
$ npm run test-normal

> mocha test/test.js



作ったプログラムを次の項目ごとにテストします
1. 挨拶のテスト その1
✓ 「やあ!」と挨拶しないとだめ
2. 挨拶のテスト その2
✓ 「はろー!」と挨拶しないとだめ
3. 挨拶と合計を計算するテスト
✓ 挨拶と合計を計算しないとだめ


3 passing (1ms)

3 passingと表示されれば、本体のプログラム./index.jsと外部モジュール./lib/sample-lib.jsに亘る3つのメソッドが問題なく動作することが検証された事になります。

まとめ

という事で、今回はjavascriptやES6における、テストの書き方を簡単に解説していきました。期待した通りにプログラムが動作するか、テストコードで検証する事によって、プログラムの動作を隈なく検証することができます。

プログラムが隈なくチェックされているのか?を計測できるテスト網羅率(カバレッジ)取得という手法がありますので、次回はカバレッジについて触れていきたいと思います。