授權方式(Auhorization): CC-BY 4.0

用 redux 架構寫的 code ,要撰寫 test 也非常簡單。

參照 redux/examples/todomvc。

使用到的 npm 套件

  1. mocha
  2. expect
  3. mocha-jsdom

目錄架構

test
├── actions
|   └── todos.spec.js 
├── components
|   ├── Header.spec.js
|   └── Footer.spec.js
├── reducers
|   └── todos.spec.js
├── utils
|   ├── util.spec.js
|   └── other.spec.js
├── jsdomReact.js
└── compiler.js

action spec

因為是 pure function 所以撰寫起來非常簡單。

action.spec.js
it('changeSomething should create CHANGE_SOMETHING action', () => {
  expect(actions.changeSomething('name', 'Ly Cheng')).toEqual({
    type: types.CHANGE_SOMETHING,
    field: 'name',
    value: 'Ly Cheng'
  });
});

reducer spec

reducer 也是 pure function 所以把 state 跟 action 兩個 object 丟進去,結果是你要的 object 就可以了。

reducer.spec.js
it('should handle initial state', () => {
  expect(
    todos(undefined, {})
  ).toEqual({
    name: ''
  });
});

it('should handle CHANGE_SOMETHING', () => {
  expect(
    todos(
      {name: ''},
      {type: types.CHANGE_SOMETHING, field: 'name', value:'Ly Cheng'})
  ).toEqual({'name', 'Ly Cheng'});
});

如果加上 immutable 的話,可以這樣寫

immutable-reducer.spec.js
const state = Immutable.fromJS({name: ''});
it('should handle initial state', () => {
  expect(
    todos(undefined, {})
  ).toEqual(state);
});

it('should handle CHANGE_SOMETHING', () => {
  expect(
    todos(
      state,
      {type: types.CHANGE_SOMETHING, field: 'name', value:'Ly Cheng'})
  ).toEqual(state.set('name', 'Ly Cheng'));
  });

Component spec

要先設置 jsdomReact.js,並且使用 shallowRender 的方式來測試 Component。

Header.js
render() {
    return (
      <div>
        <h1 className="Name">{name}</h1>
      </div>
  );
}
Header.spec.js
function setup() {
  const props = {
    state: {
        name: ''
    },
    actions: {
      changeSomething: expect.createSpy()
    }
  };
  const renderer = TestUtils.createRenderer();
  renderer.render(<Header {...props} />);
  const output = renderer.getRenderOutput();

  return {
    props: props,
    output: output,
    renderer: renderer
  };
}
// before all 之前要先 jsdomReact() 

it('should render Name correctly', () => {
  const { output, props } = setup();
  const Name = output.props.children[0];
  expect(Name.type).toBe('h1');
  expect(Name.props.className).toBe('Name');
  expect(Name.props.children).toBe('');
});

指令

$ mocha --recursive --compilers js:./test/compiler.js

Code Coverage

有了 test 之後,就可以找 code coverage tool 來看看, code 是否都有正式測試到。
使用 isparta , isparta 基於 istanbul 搭配 babel ,於是可以很簡單的測試 ES2015 的 code 。
秘訣就是使用 babel-node

一般 command line 使用
$ ./node_modules/.bin/babel-node ./node_modules/.bin/isparta cover --report text --report html ./node_modules/.bin/_mocha -- --recursive --compilers js:./test/compiler.js

寫在 package.json 的時候
$ babel-node ./node_modules/.bin/isparta cover --report text --report html _mocha --

使用 npm_package_config 簡化,並且包含 jsx,搭配 .istanbul.yml。

package.json
{
  ...
  "scripts": {
    "test": "mocha $npm_package_config_mocha",
    "test:cov": "babel-node ./node_modules/.bin/isparta $npm_package_config_isparta cover _mocha -- $npm_package_config_mocha"
  },
  "config": {
    "isparta": "--report text --report html --include \"**/*.js\" --include \"**/*.jsx\"",
    "mocha": "--recursive --compilers js:./test/compiler.js"
  }
}

要使用 include 請務必注意是 isparta 3.0.4。
3.0.3 以前的版本無法正確解讀 --include 也不會把 istanbul.yml裡面的設定正確的放入 istanbul hook。
所以我送了一個小小的 Pull Request。

.istanbul.yml
instrumentation:

  root: src

  extensions: ['.js', '.jsx']

其他檔案

如果在 component 中會 import css,可以這樣讓 mocha 忽略這個 css 檔案。
若是使用 karma-webpack ,就在你的 karma.conf.js 加入 css 相關的 loader。

compiler.js
// https://gist.github.com/daviferreira/1503ce0532abca270b86

require('babel-core/register');

require.extensions['.css'] = function () {
  return null;
};

jsdom 也有其他 setup 方式,這邊不詳述。

jsdomReact.js
import ExecutionEnvironment from 'react/lib/ExecutionEnvironment';
import jsdom from 'mocha-jsdom';

export default function jsdomReact() {
  jsdom();
  ExecutionEnvironment.canUseDOM = true;
}

總之,如此可以開始寫測試,並且跑 code coverage 囉!
isparta 會在 command line 介面告訴你測試結果,也可以像 istanbul 一樣打開 coverage/index.html

測試愉快!