单元测试初体验
博客背景:单元测试作为今年的全组通用任务,要求在所有项目中实施,每个人都需要会写单元测试。所以我在上周进行了一下单元测试的调研,这次调研的方向是主要使用 Mocha 基于 Karma 进行包括 UI 层的单元测试。
下面我主要描述一下搭建这套单元测试环境和开发的所用技术,和具体的 demo。
使用的工具介绍
使用 JavaScript 测试执行过程管理工具 Karma
Karma是一个基于 Node.js 的 JavaScript 测试执行过程管理工具(Test Runner)。该工具可用于测试所有主流Web浏览器。这个测试工具的一个强大特性就是,它可以监控(Watch)文件的变化,然后自行执行,通过 console.log 显示测试结果。单元测试框架 Mocha
Mocha 是 JavaScript 的一种单元测试框架,既可以在浏览器环境下运行,也可以在 Node.js 环境下运行。断言库 Chai
Chai 是一个针对 Node.js 和浏览器的行为驱动测试和测试驱动测试的断言库,可与任何 JavaScript 测试框架集成。测试辅助工具 Sinon
Sinon 是一个独立的 JavaScript 测试 spy, stub, mock库,没有依赖任何单元测试框架工程。
Karma 的部分 API
1 | // karma.conf.js |
Mocha 的部分 API
1 | describe('标题', function() { |
Chai 的部分 API
Chai 支持 BDD 风格的 expect/should API 和 TDD 风格的 Assert API。
expect 和 should是 BDD 风格的,二者使用相同的链式语言来组织断言,但不同在于他们初始化断言的方式:expect 使用构造函数来创建断言对象实例,而 should 通过为 Object.prototype 新增方法来实现断言(所以 should 不支持 IE);expect 直接指向chai.expect,而 should 则是 chai.should()。
语言链
下面的接口是单纯作为语言链提供以期提高断言的可读性。除非被插件改写否则它们一般不提供测试功能。
to be been is that which and has have with at of same
.not
对之后的断言取反
1 | expect(foo).to.not.equal('bar') |
.deep
设置 deep 标记,然后使用 equal 和 property 断言。该标记可以让其后的断言不是比较对象本身,而是递归比较对象的键值对。
1 | expect(foo).to.deep.equal({ bar: 'baz'}) |
.a(type) / .an(type)
type:String,被测试的值的类型
a 和 an 断言即可作为语言链又可作为断言使用
1 | // 类型断言 |
.include(value) / contains(value)
value:Object | String | Number
include() 和 contains() 即可作为属性类断言前缀语言链又可作为作为判断数组、字符串是否包含某值的断言使用。当作为语言链使用时,常用于 key() 断言之前
1 | expect([1, 2, 3]).to.include(2) |
.ok
断言目标为真值。
1 | expect('everything').to.be.ok |
.true
断言目标为 true。注意,这里与 ok 的区别是不进行类型转换,只能为 true 才能通过断言
1 | expect(true).to.be.true |
.false
断言目标为 false
1 | expect(false).to.be.false |
.NaN
断言目标为非数字 NaN
1 | expect('foo').to.be.null |
.exist
断言目标存在,即非 null 也非 undefined
1 | var foo = 'hi', |
.empty
断言目标的长度为 0。对于数组和字符串,它检查 length 属性,对于对象,它检查可枚举属性的数量
1 | expect([]).to.be.empty |
Sinon API 介绍
辅助工具库 Sinon 主要有三个Api:spy, stub, mock
spy
翻译过来的意思是 “监视”。
sinon.js 中 spy 主要用来监视函数的调用情况,sinon 对待监视的函数进行 wrap 包装,因此可以通过它清楚的知道,该函数被调用过几次,传入什么参数返回什么结果,甚至是抛出的异常情况。
1 | var spy = sinon.spy(orginObj, 'launch'); |
当 spy 使用完成后,切记把它恢复成原始函数,就像上边例子中最后一步那样。如果不这样做,你的测试可能会出现不可预知的结果。
stub
使用 stub 来嵌入或者直接替换掉一些代码,来达到隔离的目的。stub 是代码的一部分。在运行时用 stub 替换真正代码,忽略调用代码的原有实现。目的是用一个简单一点的行为替换一个复杂的行为,从而独立地测试代码的某一部分。它拥有 spy 提供的所有功能,区别在于它会完全替换掉目标函数,而不只是记录函数的调用信息。换句话说,当使用 spy 时,原函数还会继续执行,但使用 stub 时就不会。
Mocks
Mocks 是使用 stub 的另一种途径。如果你曾经听过“mock 对象”这种说法,这其实是一码事 —— Sinon 的 mock 可以用来替换整个对象以改变其行为,就像函数 stub 一样。
单元测试 Demo
这里的一些 Demo,结合了公司内部的代码进行了实际单元测试的书写,因为涉及公司业务代码,暂不公开。请前往公司 gitlab 查看相关 Demo。
正常单元测试,git地址:https://git.ms.netease.com/changxiao/unitTest
基于 Vue 开发的组件进行 UI 层测试,主要测试 Dom 的改变,事件的触发。git 地址:https://git.ms.netease.com/guessing/fe/tree/f_unit
参考
https://mochajs.org/
https://cn.vuejs.org/v2/guide/unit-testing.html
http://blog.csdn.net/maomaolaoshi/article/details/78542837
https://www.cnblogs.com/wyqlxy/p/7131079.html
http://blog.csdn.net/hustzw07/article/details/74178051
http://www.zcfy.cc/article/sinon-tutorial-javascript-testing-with-mocks-spies-stubs-422.html