テスト
- mocha
- expect
- jest
- karma
- enzyme
テスト用のライブラリは色々ありますが、React関係のプロジェクトでよく使われているものだけ取り上げます。
mocha
- 公式サイト
- es6でテストコードを書く場合はbabel-registerが必要です
$ npm install --save-dev mocha
# es6でテストを書く場合はインストール
$ npm install --save-dev babel-register
テストコードを書きます。
import expect from 'expect'
describe('add', () => {
let add
beforeEach(() => {
add = (x, y) => {
return x + y
}
})
afterEach(() => {
add = () => {}
})
it('add(1, 2)', (done) => {
expect(add(1, 2)).toEqual(3)
done()
})
})
あとはpackage.jsonのscriptでtestを追加して動かします。
{
...,
"scripts": {
"clean": "rimraf lib dist",
"compile": "babel --presets es2015 src/ -d lib/",
"build": "cross-env NODE_ENV=production webpack",
"build:dev": "npm run compile && npm run build",
"lint": "eslint src",
// mochaはデフォルトではtest/を探索するので、別のディレクトリを探索してほしいときは
// --recursiveのあとにそのディレクトリを指定します
// --compilersで指定のフォーマットにコンパイルしてくれます
"test": "mocha --compilers js:babel-register --recursive"
},
...,
}
$ npm run test
expect
- 公式サイト
- assertionのライブラリです
$ npm install --save-dev expect
toBe, toEqualなどは直感的に使えそうなので、Spyを少し詳しく見てみる。
Spy
export function createSpy(fn, restore = noop) {
if (fn == null)
fn = noop
assert(
isFunction(fn),
'createSpy needs a function'
)
let targetFn, thrownValue, returnValue
const spy = function spy() {
spy.calls.push({
context: this,
arguments: Array.prototype.slice.call(arguments, 0)
})
if (targetFn)
return targetFn.apply(this, arguments)
if (thrownValue)
throw thrownValue
return returnValue
}
spy.calls = []
spy.andCall = (otherFn) => {
targetFn = otherFn
return spy
}
spy.andCallThrough = () =>
spy.andCall(fn)
spy.andThrow = (object) => {
thrownValue = object
return spy
}
spy.andReturn = (value) => {
returnValue = value
return spy
}
spy.getLastCall = () =>
spy.calls[spy.calls.length - 1]
spy.reset = () => {
spy.calls = []
}
spy.restore = spy.destroy = restore
spy.__isSpy = true
spies.push(spy)
return spy
}
createSpyでspyのconstructorを返す。引数にfnを指定するとaddCallThrough()でfnの返り値を取れる。addCall(fn)はspy作成後に好きなfunctionを設定できる。
export function spyOn(object, methodName) {
const original = object[methodName]
if (!isSpy(original)) {
assert(
isFunction(original),
'Cannot spyOn the %s property; it is not a function',
methodName
)
object[methodName] = createSpy(original, () => {
object[methodName] = original
})
}
return object[methodName]
}
object[methodName]をspyにする。addCallThrough()で元のfucntionが実行出来る。addCall(fn)で好きなfunctionに入れ替えられる。
一度試してみる。
import expect, { createSpy, spyOn } from 'expect'
describe('add', () => {
const calc = {
add: () => {},
multiply: () => {}
}
let add
beforeEach(() => {
add = (x, y) => {
return x + y
}
calc.add = add
})
afterEach(() => {
add = () => {}
calc.add = add
})
it('add(1, 2)', (done) => {
expect(calc.add(1, 2)).toEqual(3)
done()
})
it('add spy', (done) => {
// calc.addとspyは同じ参照を指す
// calc.add(1, 2)はspy(1, 2)なのでspy.callsに結果がpushされる
// その結果、spyがspy.callsから結果をのぞける
let spy = spyOn(calc, 'add')
expect(spy.andReturn(3).call(this)).toEqual(3)
// 元のcalc.addを使う
expect(spy.andCallThrough().call(this, 1, 2)).toEqual(3)
// functionの入れ替え
expect(spy.andCall((x, y) => {
return x *2 + y * 2
}).call(this, 1, 2)).toEqual(6)
done()
})
})
想定通りの結果となった。
jest
- 公式サイト
- facebook製のUnit testライブラリ。assertionにはjasmineというのを使用しているようだ。
- これ一つで何でもできるらしい。mocha+expect+karma相当?
$ npm install --save-dev jest-cli
# babelを使用する場合合わせてインストール
$ npm install --save-dev babel-jest babel-polyfill
npm runで実行できるようにする。
babelを使う場合はjestのpropertyを作成する。
{
...,
"scripts": {
...,
"test": "mocha --compilers js:babel-register --recursive",
"jtest": "jest"
},
// babelを使う場合に追加
"jest": {
"scriptPreprocessor": "<rootDir>/node_modules/babel-jest",
"testFileExtensions": ["es6", "js"],
"moduleFileExtensions": ["js", "json", "es6"]
},
...,
}
__tests__
ディレクトリを作成してテストを書く。
// 特にexpectをimportする必要はない
describe('add', () => {
const calc = {
add: () => {},
multiply: () => {}
}
let add
beforeEach(() => {
add = (x, y) => {
return x + y
}
calc.add = add
})
afterEach(() => {
add = () => {}
calc.add = add
})
it('add(1, 2)', (done) => {
expect(calc.add(1, 2)).toEqual(3)
done()
})
/* expectしか使えない?
it('add spy', (done) => {
let spy = expect.spyOn(calc, 'add')
expect(spy.andReturn(3).call(this)).toEqual(3)
expect(spy.andCallThrough().call(this, 1, 2)).toEqual(3)
expect(spy.andCall((x, y) => {
return x *2 + y * 2
}).call(this, 1, 2)).toEqual(6)
done()
})
*/
})
どうやらexpect()しか使えないようだ。。別のライブラリを使ってる可能性ありそう・・
$ npm run jtest
karma
- 公式サイト
- ブラウザ上でファイルを実行してくれる
# mochaとwebpackを使用することを想定
$ npm install --save-dev karma karma-chrome-launcher karma-mocha karma-webpack
configファイルを初期化します。
$ ./node_modules/karma/bin/karma init karma.conf.js
Which testing framework do you want to use ?
Press tab to list possible options. Enter to move to the next question.
> mocha
Do you want to use Require.js ?
This will add Require.js plugin.
Press tab to list possible options. Enter to move to the next question.
> no
Do you want to capture any browsers automatically ?
Press tab to list possible options. Enter empty string to move to the next question.
> Chrome
>
What is the location of your source and test files ?
You can use glob patterns, eg. "js/*.js" or "test/**/*Spec.js".
Enter empty string to move to the next question.
> test/**/*spec.js
>
Should any of the files included by the previous patterns be excluded ?
You can use glob patterns, eg. "**/*.swp".
Enter empty string to move to the next question.
>
Do you want Karma to watch all the files and run the tests on change ?
Press tab to list possible options.
> yes
webpackでes6をコンパイルするので、configファイルを修正します。
module.exports = function(config) {
config.set({
...,
// webpack指定
preprocessors: {
'test/**/*.js': ['webpack']
},
// es6をコンパイルするように設定
webpack: {
module: {
loaders: [
{
test: /\.js$/,
loaders: ['babel-loader?presets[]=react&presets[]=es2015&cacheDirectory'],
exclude: /node_modules/
}
]
},
},
...,
})
}
npm runで実行できるようにする。
{
...,
"scripts": {
...,
"test": "mocha --compilers js:babel-register --recursive",
"jtest": "jest",
"test-browser": "karma start"
},
...,
}
$ npm run test-browser
enzyme
- 公式サイト
- 5分でなんとなくわかる動画
- Reactをテストすることに特化したTestライブラリ
$ npm install --save-dev enzyme
# 依存するライブラリのインストール
$ npm install --save-dev react-addons-test-utils react-dom
軽く試してみる。
import React from 'react'
export default React.createClass({
render() {
return <div>About</div>
}
})
import React from 'react'
import expect from 'expect'
import { shallow } from 'enzyme'
import About from '../src/react-router/modules/About'
describe('<About />', () => {
it('should render there <About />', () => {
const about = shallow(<About />)
expect(about.find('div').length).toEqual(1)
})
})
これでmochaを実行する。
アプリケーションがReact前提なら便利かも。
テストは色々なライブラリやフレームワークがあってどれを使うか悩みますね!