在这里说一下前端开发的一个特点是更多的会涉及用户界面,当开发规模达到一定程度时,几乎注定了其复杂度会成倍的增长。
无论是在代码的初始搭建过程中,还是之后难以避免的重构和修正bug过程中,常常会陷入逻辑难以梳理、无法掌握全局关联的境地。
而单元测试作为一种“提纲挈领、保驾护航”的基础手段,为开发提供了“围墙和脚手架”,可以有效的改善这些问题。
作为一种经典的开发和重构手段,单元测试在软件开发领域被广泛认可和采用;前端领域也逐渐积累起了丰富的测试框架和最佳实践。
本文将按如下顺序进行说明:
- I. 单元测试简介
- II. React 单元测试中用到的工具
- III. 用测试驱动 React 组件重构
- IV. React 单元测试常见案例
I. 单元测试简介
单元测试(unit testing),是指对软件中的最小可测试单元进行检查和验证。
简单来说,单元就是人为规定的最小的被测功能模块。单元测试是在软件开发过程中要进行的最低级别的测试活动,软件的独立单元将在与程序的其他部分相隔离的情况下进行测试。
测试框架
测试框架的作用是提供一些方便的语法来描述测试用例,以及对用例进行分组。
断言(assertions)
断言是单元测试框架中核心的部分,断言失败会导致测试不通过,或报告错误信息。
对于常见的断言,举一些例子如下:
-
同等性断言 Equality Asserts
- expect(sth).toEqual(value)
- expect(sth).not.toEqual(value)
-
比较性断言 Comparison Asserts
- expect(sth).toBeGreaterThan(number)
- expect(sth).toBeLessThanOrEqual(number)
-
类型性断言 Type Asserts
- expect(sth).toBeInstanceOf(Class)
-
条件性测试 Condition Test
- expect(sth).toBeTruthy()
- expect(sth).toBeFalsy()
- expect(sth).toBeDefined()
断言库
断言库主要提供上述断言的语义化方法,用于对参与测试的值做各种各样的判断。这些语义化方法会返回测试的结果,要么成功、要么失败。常见的断言库有 Should.js, Chai.js 等。
测试用例 test case
为某个特殊目标而编制的一组测试输入、执行条件以及预期结果,以便测试某个程序路径或核实是否满足某个特定需求。
一般的形式为:
it('should ...', function() {
...
expect(sth).toEqual(sth);
});
测试套件 test suite
通常把一组相关的测试称为一个测试套件
一般的形式为:
describe('test ...', function() {
it('should ...', function() { ... });
it('should ...', function() { ... });
...
});
spy
正如
spy
字面的意思一样,我们用这种“间谍”来“监视”函数的调用情况
通过对监视的函数进行包装,可以通过它清楚的知道该函数被调用过几次、传入什么参数、返回什么结果,甚至是抛出的异常情况。
var spy = sinon.spy(MyComp.prototype, 'componentDidMount');
...
expect(spy.callCount).toEqual(1);
stub
有时候会使用
stub
来嵌入或者直接替换掉一些代码,来达到隔离的目的
一个stub
可以使用最少的依赖方法来模拟该单元测试。比如一个方法可能依赖另一个方法的执行,而后者对我们来说是透明的。好的做法是使用stub 对它进行隔离替换。这样就实现了更准确的单元测试。
var myObj = {
prop: function() {
return 'foo';
}
};
sinon.stub(myObj, 'prop').callsFake(function() {
return 'bar';
});
myObj.prop(); // 'bar'
mock
mock
一般指在测试过程中,对于某些不容易构造或者不容易获取的对象,用一个虚拟的对象来创建以便测试的测试方法
广义的讲,以上的 spy 和 stub 等,以及一些对模块的模拟,对 ajax 返回值的模拟、对 timer 的模拟,都叫做 mock 。