当前时代,前端技术的飞速发展与更迭,前端框架的百花齐放,前端从业人员激增,做为一个有素养,有理想的前端童鞋如何保证本身的代码质量,让本身的代码更加严谨、可靠成了咱们绕不过去的话题,这时候就离不开测试,固然做为一个开发人员的话咱们更注重的应该是单元测试。javascript
ATDD,TDD,BDD
聊到了测试,那咱们先简单提几个概念:ATDD,TDD,BDD.html
ATDD: Acceptance Test Driven Development(验收测试驱动开发):前端
这是一种在编码开始以前将客户带入测试设计过程的技术。它也是一个协做实践,用户,测试人员和开发人员定义了自动验收标准。若是系统未经过测试可提供快速反馈,说明未知足要求。验收测试以业务领域术语进行指定。每一个功能都必须提供真实且可衡量的业务价值。java
TDD: Test-driven development (测试驱动开发):ios
是一种使用自动化单元测试来推进软件设计并强制依赖关系解耦的技术。在编写真正实现功能的代码以前先编写测试,每次测试以后,重构完成,而后再次执行相同或相似的测试。该过程根据须要重复屡次,直到每一个单元根据所需的规格运行。正则表达式
BDD:Behavior-Driven Development (行为驱动开发):npm
是一种敏捷软件开发的技术,它鼓励软件项目中的开发者、QA和非技术人员或商业参与者之间的协做。json
使用BDD的团队应该可以以用户故事的形式提供大量的“功能文档”,并增长可执行场景或示例。 BDD一般有助于领域专家理解实现而不是暴露代码级别测试。它一般以GWT格式定义:GIVEN WHEN&THEN。axios
测试工具的类型
组合使用工具很常见,即便已选框架也能实现相似的功能数组
- 提供测试框架(Mocha, Jasmine, Jest, Cucumber)
- 提供断言(Chai, Jasmine, Jest, Unexpected)
- 生成,展现测试结果(Mocha, Jasmine, Jest, Karma)
- 快照测试(Jest, Ava)
- 提供仿真(Sinon, Jasmine, enzyme, Jest, testdouble)
- 生成测试覆盖率报告(Istanbul, Jest, Blanket)
- 提供类浏览器环境(Protractor, Nightwatch, Phantom, Casper)
不一样测试框架的对比
Jest
- facebook 坐庄
- 基于 Jasmine 至今已经作了大量修改添加了不少特性
- 开箱即用配置少,API简单
- 支持断言和仿真
- 支持快照测试
- 在隔离环境下测试
- 互动模式选择要测试的模块
- 优雅的测试覆盖率报告,基于Istanbul
- 智能并行测试
- 较新,社区不十分红熟
- 全局环境,好比 describe 不须要引入直接用
- 较多用于 React 项目(但普遍支持各类项目)
Mocha
- 灵活(不包括断言和仿真,本身选对应工具) 流行的选择:chai,sinon
- 社区成熟用的人多,测试各类东西社区都有示例
- 须要较多配置
- 可使用快照测试,但依然须要额外配置
Jasmine
- 开箱即用(支持断言和仿真)
- 全局环境
- 比较'老',坑基本都有人踩过了
AVA
- 异步,性能好
- 简约,清晰
- 快照测试和断言须要三方支持
Tape
- 体积最小,只提供最关键的东西
- 对比其余框架,只提供最底层的 API
总结一下,Mocha 用的人最多,社区最成熟,灵活,可配置性强易拓展,Jest 开箱即用,里边啥都有提供全面的方案,Tape 最精简,提供最基础的东西最底层的API。
下面开始今天主要的话题,前端单元测试jest.js的学习使用
Jest 做为 Facebook 的一套开源的 JavaScript 测试框架, 它自动集成了断言、JSDom、覆盖率报告等开发者所须要的全部测试工具,是一款几乎零配置的测试框架。
单元测试主要思想与步骤
-
编写测试用例
即描述单元测试要作什么,给定一个输入,定义指望的输出,检验要测试的代码是否符合指望
-
编写被测试代码
-
执行测试,若是未经过测试,则修改被测试代码直到测试经过
初始化与安装
// 新建个项目并初始化 mkdir study-jest && cd npm init -y // 下载安装jest npm i jest -D // 配置script脚本,以便经过命令行运行测试 "scripts": { "jest-t": "jest" } npm install -D babel-jest babel-core babel-preset-env regenerator-runtime /* babel-jest、 babel-core、 regenerator-runtime、babel-preset-env这几个依赖是为了让咱们能够 使用ES6的语法特性进行单元测试,ES6提供的 import 来导入模块的方式,Jest自己是不支持的。*/ // 配置.babelrc文件 { "presets": ["env"] }
first blood
默认状况下,咱们的测试文件通常放在项目中的tests
文件夹中,而且咱们测试文件命名通常是xx.spec.js
,因此建立一个tests
文件夹,并在里边建立一个名为firstTest.spec.js
的文件来编写咱们的第一个测试用例
如今咱们要实现一个sum函数用来作整数的相加,首先咱们来编写他的测试用例
// firstTest.spec.js // 描述要测试的模块 test('sum(1 + 1) 等于 2', () => { // 断言:即指望运行结果 expect(functions.sum(1, 1)).toBe(2); });
编写被测试代码
// functions.js export default { sum(a, b) { // return a; // 错误逻辑 return a + b; } }
编写完成以后执行测试指令npm run jest-t
测试成功输出
测试失败输出
经常使用的几个Jest断言
上面测试用例中的expect(functions.sum(2, 2)).toBe(4)
为一句断言,Jest为咱们提供了expect
函数用来包装被测试的方法并返回一个对象,该对象中包含一系列的匹配器来让咱们更方便的进行断言,上面的toBe
函数即为一个匹配器。咱们来介绍几种经常使用的Jest断言,其中会涉及多个匹配器。
.not
// firstTest.spec.js // .not修饰符容许你测试结果不等于某个值的状况 test('sum(1 + 1) 不等于 3', () => { expect(functions.sum(1, 1)).not.toBe(3); });
.toEqual()
// functions.js export default { getObj() { return { a: 1, b: 2 }; } };
// firstTest.spec.js /* .toEqual匹配器会递归的检查对象全部属性和属性值是否相等,因此若是要进行应用类型的比较 时,请使用.toEqual而不是.toBe */ test('getObj() 返回的对象深度相等', () => { expect(functions.getObj()).toEqual(functions.getObj()); }); test('getObj()返回的对象内存地址不一样', () => { expect(functions.getObj()).not.toBe(functions.getObj()); });
.toHaveLength
// functions.js export default { getIntArray(num) { if (!Number.isInteger(num)) { throw Error('"getIntArray"只接受整数类型的参数'); } let result = []; for (let i = 0, len = num; i < len; i++) { result.push(i); } return result; }; }
// firstTest.spec.js // .toHaveLength能够很方便的用来测试字符串和数组类型的长度是否知足预期。 test('getIntArray(3)返回的数组长度应该为3', () => { expect(functions.getIntArray(3)).toHaveLength(3); });
.toThrow
// firstTest.spec.js /* .toThrow可可以让咱们测试被测试方法是否按照预期抛出异常,可是在使用时须要注意的是:咱们 必须使用一个函数将将被测试的函数作一个包装,正如上面getIntArrayWrapFn所作的那样,不然会 由于函数抛出致使该断言失败。*/ test('getIntArray(3.3)应该抛出错误', () => { function getIntArrayWrapFn() { functions.getIntArray(3.3); } expect(getIntArrayWrapFn).toThrow('"getIntArray"只接受整数类型的参数'); });
.toMatch
// firstTest.spec.js // .toMatch传入一个正则表达式,它容许咱们用来进行字符串类型的正则匹配。 test('getObj().a应该为数字', () => { expect(functions.getObj().a).toMatch(/\d/i); });
double kill
异步函数测试
这里咱们使用最经常使用的http请求库axios
来进行请求处理
npm install axios
编写http请求函数 咱们将请求http://jsonplaceholder.typicode.com/users/1
,这是由JSONPlaceholder提供的mock请求地址
// asyncFn.js import axios from 'axios'; export default { fetchUser() { return axios.get('http://jsonplaceholder.typicode.com/users/1') .then(res => res.data) .catch(error => console.log(error)); } }
// secondTest.js test('fetchUser() 能够请求到一个含有name属性值为Leanne Graham的对象', () => { expect.assertions(1); return asyncFn.fetchUser() .then(data => { expect(data.name).toBe('Leanne Graham'); }); }); /* 上面咱们调用了expect.assertions(1),它能确保在异步的测试用例中,有一个断言会在回调函数中 被执行。这在进行异步代码的测试中十分有效。*/
代码覆盖率
代码覆盖率指的是咱们写的代码有没有单元测试所覆盖到。Jest
具备内置的代码覆盖范围,你能够经过两种方式激活它:
- 经过命令行传递标志
--coverage
- 在
package.json
中配置Jest
// 方式一:命令行 npm run jest-t -- --coverage // 方式二:配置package.json中的jest "jest": { "collectCoverage": true } // 方式三:配置package.json中的script指令 "scripts": { "jest-t": "jest --coverage" },
测试完毕报告
咱们每次运行完npm run jest-t
时能够在项目中看到一个名为coverage
的文件夹,你能够经过访问coverage/lcov-report/index.html
来查看详细的测试报告他能够明确的指出未经测试的代码。 详细报告
参考资料
【1】 Jest官方文档(https://jestjs.io/zh-Hans/)
【2】李棠辉-简书(https://www.jianshu.com/p/70a4f026a0f1)