initial
下面两篇文章值得一看:
Testing in ES6 with Mocha and Babel 6
Using Babel
setup
$ npm i mocha --save-dev
$ npm i chai --save-dev
Use with es6
babel 6+
$ npm install --save-dev babel-register $ npm install babel-preset-es2015 --save-dev // package.json { "scripts": { "test": "./node_modules/mocha/bin/mocha --compilers js:babel-register" }, "babel": { "presets": [ "es2015" ] } } |
babel 5+
$ npm install --save-dev babel-core // package.json { "scripts": { "test": "./node_modules/mocha/bin/mocha --compilers js:babel-core/register" } } Use with coffeescript $ npm install --save coffee-script { "scripts": { "test": "./node_modules/mocha/bin/mocha --compilers coffee:coffee-script/register" } } |
Use with es6+coffeescript
After done both...
{ "scripts": { "test": "./node_modules/mocha/bin/mocha --compilers js:babel-core/register,coffee:coffee-script/register" } } # $ mocha $ npm t $ npm test |
chai
import chai from 'chai'; const assert = chai.assert; const expect = chai.expect; const should = chai.should(); foo.should.be.a('string'); foo.should.equal('bar'); list.should.have.length(3); obj.should.have.property('name'); expect(foo).to.be.a('string'); expect(foo).to.equal('bar'); expect(list).to.have.length(3); expect(obj).to.have.property('flavors'); assert.typeOf(foo, 'string'); assert.equal(foo, 'bar'); assert.lengthOf(list, 3); assert.property(obj, 'flavors'); |
Test
测试的一个基本思路是,自身从函数的调用者出发,对函数进行各种情况的调用,查看其容错程度、返回结果是否符合预期。
import chai from 'chai'; const assert = chai.assert; const expect = chai.expect; const should = chai.should(); describe('describe a test', () => { it('should return true', () => { let example = true; // expect expect(example).not.to.equal(false); expect(example).to.equal(true); // should example.should.equal(true); example.should.be.a(boolen); [1, 2].should.have.length(2); }); it('should check an object', () => { // 对于多层嵌套的Object而言.. let nestedObj = { a: { b: 1 } }; let nestedObjCopy = Object.assign({}, nestedObj); nestedObj.a.b = 2; // do a function to change nestedObjCopy.a.b expect(nestedObjCopy).to.deep.equal(nestedObj); expect(nestedObjCopy).to.have.property('a'); }); }); |
AsynTest
Testing Asynchronous Code with MochaJS and ES7 async/await
mocha无法自动监听异步方法的完成,需要我们在完成之后手动调用 done() 方法
而如果要在回调之后使用异步测试语句,则需要使用 try/catch 进行捕获。成功则 done() ,失败则 done(error)
// 普通的测试方法 it("should work", () =>{ console.log("Synchronous test"); }); // 异步的测试方法 it("should work", (done) =>{ setTimeout(() => { try { expect(1).not.to.equal(0); done(); // 成功 } catch (err) { done(err); // 失败 } }, 200); }); |
异步测试有两种方法完结: done 或者返回 Promise 。而通过返回 Promise ,则不再需要编写笨重的 try/catch 语句
it("Using a Promise that resolves successfully with wrong expectation!", function() {
var testPromise = new Promise(function(resolve, reject) {
setTimeout(function() {
resolve("Hello World!");
}, 200);
});
return testPromise.then(function(result){
expect(result).to.equal("Hello!");
});
});
mock
mock是一个接口模拟库,我们可以通过它来模拟代码中的一些异步操作
React单元测试
Test React Component
React组件无法直接通过上述方法进行测试,需要安装enzyme依赖。
$ npm i --save-dev enzyme
#
$ npm i --save-dev react-addons-test-utils
假设有这样一个组件:
// ...省略部分import代码 class TestComponent extends React.Component { constructor(props) { super(props); let {num} = props; this.state = { clickNum: num } this.handleClick = this.handleClick.bind(this) } handleClick() { let {clickNum} = this.state; this.setState({ clickNum: clickNum + 1 }); } render() { let {clickNum} = this.state; return ( <div className="test_component"> {clickNum} <span onClick={this.handleClick}>点我加1</span> </div> ) } } |
使用样例:
import React from 'react'; import {expect} from 'chai'; import {shallow} from 'enzyme'; import TestComponent from '../components/TestComponent'; describe('Test TestComponent', () => { // 创建一个虚拟的组件 const wrapper = shallow( <TestComponent num={10} />/ ); /* * 之后,我们可以: * 通过wrapper.state()拿到组件的state * 通过wrapper.instance()拿到组件实例,以此调用组件内的方法 * 通过wrapper.find()找到组件内的子组件 * 但是,无法通过wrapper.props()拿到组件的props */ // 测试该组件组外层的class it('should render with currect wrapper', () => { expect(wrapper.is('.test_component')).to.equal(true); }); // 测试该组件初始化的state it('should render with currect state', () => { expect(wrapper.state()).to.deep.equal({ clickNum: 10 }); }); // 测试组件的方法 it('should add one', () => { wrapper.instance().handleClick(); expect(wrapper.state()).to.deep.equal({ clickNum: 11 }); }); }); |
Test Redux
redux身为纯函数,非常便于mocha进行测试
// 测试actions import * as ACTIONS from '../redux/actions'; describe('test actions', () => { it('should return an action to create a todo', () => { let expectedAction = { type: ACTIONS.NEW_TODO, todo: 'this is a new todo' }; expect(ACTIONS.addNewTodo('this is a new todo')).to.deep.equal(expectedAction); }); }); // 测试reducer import * as REDUCERS from '../redux/reducers'; import * as ACTIONS from '../redux/actions'; describe('todos', () => { let todos = []; it('should add a new todo', () => { todos.push({ todo: 'new todo', complete: false }); expect(REDUCERS.todos(todos, { type: ACTIONS.NEW_TODO, todo: 'new todo' })).to.deep.equal([ { todo: 'new todo', complete: false } ]); }); }); // 还可以和store混用 import { createStore, applyMiddleware, combineReducers } from 'redux'; import thunk from 'redux-thunk'; import chai from 'chai'; import thunkMiddleware from 'redux-thunk'; import * as REDUCERS from '../redux/reducers'; import defaultState from '../redux/ConstValues'; import * as ACTIONS from '../redux/actions' const appReducers = combineReducers(REDUCERS); const AppStore = createStore(appReducers, defaultState, applyMiddleware(thunk)); let state = Object.assign({}, AppStore.getState()); // 一旦注册就会时刻监听state变化 const subscribeListener = (result, done) => { return AppStore.subscribe(() => { expect(AppStore.getState()).to.deep.equal(result); done(); }); }; describe('use store in unittest', () => { it('should create a todo', (done) => { // 首先取得我们的期望值 state.todos.append({ todo: 'new todo', complete: false }); // 注册state监听 let unsubscribe = subscribeListener(state, done); AppStore.dispatch(ACTIONS.addNewTodo('new todo')); // 结束之后取消监听 unsubscribe(); }); }); |
基于 phantomjs 和selenium的UI UnitTest
PhantomJS 是一个基于webkit的服务器端JavaScript API,即相当于在内存中跑了个无界面的webkit内核的浏览器。通过它我们可以模拟页面加载,并获取到页面上的DOM元素,进行一系列的操作,以此来模拟UI测试。但缺点是无法实时看见页面上的情况(不过可以截图)。
而 Selenium 是专门为Web应用程序编写的一个验收测试工具,它直接运行在浏览器中。 Selenium 测试通常会调起一个可见的界面,但也可以通过设置,让它以 PhantomJS 的形式进行无界面的测试。
· open 某个 url
· 监听 onload 事件
· 事件完成后调用 sendEvent 之类的 api 去点击某个 DOM 元素所在 point
· 触发交互
· 根据 UI 交互情况 延时 setTimeout (规避惰加载组件点不到的情况)继续 sendEvent 之类的交互