ava, sinonを使ったnode.jsのテスト

こちらの記事を読みました。

[Mocha, Chai, Sinon を使ったNode.js のテスト自動化　実践編 - Qiita](http://qiita.com/TsutomuNakamura/items/0eb50bf7622a3906e101)

上記のテストをavaで書き直してみます。
travisでv0.10,v0.12,v4,v5のすべての環境でパスするのを確認しました。


avaについては日本語のドキュメントがあるので、それを確認するといいでしょう。

[ava-docs/ja_JP at master · sindresorhus/ava-docs](https://github.com/sindresorhus/ava-docs/tree/master/ja_JP#%E6%98%8E%E5%BF%AB%E3%81%AA%E3%82%B9%E3%82%BF%E3%83%83%E3%82%AF%E3%83%88%E3%83%AC%E3%83%BC%E3%82%B9)

テスト対象のコードは以下です。
もとのコードから少し変えさせてもらってます。

```index.js
'use strict';

function calculate(num) {
	if (typeof num !== 'number' || isNaN(num)) {
		throw new TypeError('Type of numeric is expected.');
	}

	return Math.floor(num / 2);
}

function read() {
	process.openStdin().on('data', function(chunk) {
		var param = Number(chunk);
		try {
			var result = calculate(param);
			console.log('result: ' + result);
		} catch (e) {
			console.log(e.message);
		}
	});
}

exports.calculate = calculate;
exports.read = read;
```

## 環境

```
$ npm init
$ npm i -D ava
```

テスト対象のコードを`index.js`、テストファイルを`test.js`とします。
テストコマンドは以下です。

```
$ ava
```

詳細

```
$ ava -v
```

監視

```
$ ava -w
```

## 実行

内容自体は元コードままです。
avaのテストは並列で走りますが、console.logをスタブする関係で一部直列化しています。
また、avaは内部的にbabelを使っているのでes6で書けます。

```test.js
import test from 'ava';
import sinon from 'sinon';
import {EventEmitter} from 'events';
import fn from './';

test.beforeEach(t => {
	t.context.log = console.log;
	sinon.stub(console, 'log');
});

test.afterEach(t => {
	console.log = t.context.log;
});

test('return 2 when the value is 4', t => {
	t.is(fn.calculate(4), 2);
});

test('return 1 when the value is 3', t => {
	t.is(fn.calculate(3), 1);
});

test('throw exceptions when the value are other than numbers', t => {
	t.throws(fn.calculate);
	t.throws(() => fn.calculate(null), 'Type of numeric is expected.');
	t.throws(() => fn.calculate('abc'), / numeric /);
	t.throws(() => fn.calculate([]), TypeError, /^Type of numeric /);
});

test.serial('print "result: 4" when the value is 8 that given from the stdin', t => {
	const ev = new EventEmitter();
	process.openStdin = sinon.stub().returns(ev);
	fn.read();
	ev.emit('data', '8');
	t.is(console.log.callCount, 1);
	t.true(console.log.calledWith('result: 4'));
});

test.serial('print "Type of numeric is expected." when the value is not a numeric', t => {
	const ev = new EventEmitter();
	process.openStdin = sinon.stub().returns(ev);
	fn.read();
	ev.emit('data', 'abc');
	t.is(console.log.callCount, 1);
	t.true(console.log.calledWithMatch('Type of numeric is expected.'));
});
```

## 比較

chaiのshould記法とexpect記法

```js
// should
divided.calculate(4).should.equal(2);
// expect
expect(divided.calculate(4)).to.equal(2);
```

ava

```js
t.is(divided.calculate(4), 2);
```

avaの方が読みやすく簡潔ですね。
また、テストを落としたときのエラーはpower-assertにより非常にわかりやすいです。

```
  1. test › return 1 when the value is 3

  t.is(fn.calculate(3), 1)
          |
          1.5

      Test.fn (test.js:15:4)
```

## その他
今回はすべて同期処理のテストでしたが、avaはasyncが使えるので、Promiseによる非同期処理のテストが簡潔に書けます。

```test.js
import test from 'ava';

function calc(num) {
	return new Promise(resolve => {
		resolve(num * num);
	});
}

test('calc', async t => {
	const ans = await calc(10);
	t.is(ans, 100);
});
```

## 参考
- [Mocha, Chai, Sinon を使ったNode.js のテスト自動化　実践編 - Qiita](http://qiita.com/TsutomuNakamura/items/0eb50bf7622a3906e101) 
- [ava-docs/ja_JP at master · sindresorhus/ava-docs](https://github.com/sindresorhus/ava-docs/tree/master/ja_JP#%E6%98%8E%E5%BF%AB%E3%81%AA%E3%82%B9%E3%82%BF%E3%83%83%E3%82%AF%E3%83%88%E3%83%AC%E3%83%BC%E3%82%B9)
- [sindresorhus/ava: Futuristic test runner](https://github.com/sindresorhus/ava)