主题
单元测试
一. 单元测试的作用与好处
对于一个完整的前端工程来讲, 有单元测试环节是非常有必要的, 特别当你开发的是一个开源库或者插件, 拥有完整的单元测试会让使用者心里有底, 使用起来会更放心, 在同等功能的库面前, 拥有单元测试的库更受欢迎
谁也不能保证自己写的代码里面没有bug, 合适且完整的单元测试能保证将bug出现的概率降到最低, 那么除了这些, 但愿测试还有哪些好处, 我认为有以下几点:
- 自动化: 写代码时候肯定避免不了各种调试, 我们平时用的最多的调试无非就是
console.log
,debugger
, 二者在开发中使用, 代码上线时候, 需要移除这些log
和debug
, 因此不长久, 效率也不高, 编写单元测试可以做到一次编写, 多次执行,提高了效率
- 自动化: 写代码时候肯定避免不了各种调试, 我们平时用的最多的调试无非就是
- 重构保障: 当项目中有一个逻辑特别复杂的屎山代码需要重构, 你能保障重构之后结果是正确的? 逻辑都考虑完全了? 这时候若是这部分代码原先就拥有单元测试, 那重构起来就不害怕会少考虑某些逻辑, 降低心智负担
- 保证正确: 通过将某个功能的预期用测试用例的形式编写出来, 功能编写完只需要跑一下测试用例就知道功能是否正常
- 减少缺陷率: 一个正常团队, 对缺陷率肯定是有一定的指标的, 若你写一个功能, 都没有自测就甩给测试同学, 那么测试同学肯定会还给你一堆bug, 你也会被测试同学被列入重点关注对象, 因为你到代码很不靠谱, 简单的1 + 1 都会等于 3
前端愿意做单元测试的团队不多, 有的是因为没这方面的意识, 有的压根就不知道单元测试该如何做, 还有的是因为敏捷开发,更多的时间用来完成需求, 快速上线, 根本没时间来做单元测试, 系统稳定性只有靠测试人员的运气和负责任程度来保障
二. 常见测试框和断言库
js测试框架有很多, 目前用的最多的前端单元测试框架主要有mocha
和 jest
,但推荐你使用 Jest
, 因为 Jest
和 Mocha
相比,无论从github starts
& issues
量,npm下载量
相比,都有明显优势。
下面是两个框架的对比图, 图片来源传送门
框架 | 断言 | 异步 | 代码覆盖率 |
---|---|---|---|
Mocha | 不支持(需要其他库支持) | 友好 | 不支持(需要其他库支持) |
Jest | 默认支持 | 友好 | 支持 |
Mocha
生态好,但是需要较多的配置来实现高扩展性Jest
开箱即用, 自带断言以及测试覆盖率
1. Mocha
+ Chai
Mocha
需要引入其他断言库去断言, 例如 chai(chai是第三方模块) assert
(assert是nodejs自身的断言模块)
Chai 是一个断言库,类似于 Node 的内置assert。它为您提供了大量可以针对您的代码运行的断言,从而使测试变得更加容易。
mocha + assert
示例
js
const assert = require("assert");
describe("1. 这是一个测试用例的组", function () {
describe("组内的第一小组", function () {
it("测试1+1", function () {
assert.equal(1 + 1, 2);
});
it("测试1+2", function () {
assert.equal(1 + 2, 2);
});
});
});
scripts配置命令test运行mocha index.js
输出如下结果:
bash
> mocha ./index.js
1. 这是一个测试用例的组
组内的第一小组
✔ 测试1+1
1) 测试1+2
1 passing (5ms)
1 failing
1) 1. 这是一个测试用例的组
组内的第一小组
测试1+2:
AssertionError [ERR_ASSERTION]: 3 == 2
+ expected - actual
-3
+2
mocha + chai
示例
js
const { expect } = require("chai");
describe("1. 这是一个测试用例的组", function () {
describe("组内的第一小组", function () {
it("测试1+1", function () {
expect(1 + 1).to.eq(2);
});
it("测试1+2", function () {
expect(1 + 2).to.equal(2);
});
});
});
2. jest
js
const { expect } = require("@jest/globals");
describe("1. 这是一个测试用例的组", function () {
describe("组内的第一小组", function () {
it("测试1+1", function () {
expect(1 + 1).toBe(2);
});
it("测试1+2", function () {
expect(1 + 2).toEqual(2);
});
});
});
scripts配置命令test运行jest
, jest默认会找/test/**.spec.js
或者/test/**.test.js
文件
输出如下结果:
bash
> jest
FAIL test/index.spec.js
1. 这是一个测试用例的组
组内的第一小组
√ 测试1+1 (3 ms)
× 测试1+2 (2 ms)
● 1. 这是一个测试用例的组 › 组内的第一小组 › 测试1+2
expect(received).toEqual(expected) // deep equality
Expected: 2
Received: 3
9 |
10 | it("测试1+2", function () {
> 11 | expect(1 + 2).toEqual(2);
| ^
12 | });
13 | });
14 | });
at Object.toEqual (test/index.spec.js:11:21)
Test Suites: 1 failed, 1 total
Tests: 1 failed, 1 passed, 2 total
Snapshots: 0 total
Time: 0.378 s
Ran all test suites.
Jest 默认支持断言,同时默认支持覆盖率测试
三. 测试框架和断言库各自的作用
it
/test
从上面测试代码可以看出 无论哪个测试框架, 测试用例的书写都是差不多的, 每一个测试用例用it()
(有的是test()
)包起来
it
或者test
是测试框架提供的函数, 运行测试框架的时候会自动执行it
函数, it
函数第一个参数是标识这个测试用例的干嘛的, 第二个参数是一个回调函数, 也是测试用例的具体逻辑代码
describe
describe
可以用于将一个文件中的测试用例分成多个组, 便于在输出测试结果的时候按照组进行输出, 也是由测试框架提供的
expect
断言, 每一个断言库都会提供一个expect
函数, 具体使用方法以及API需要参照不同断言库, 例如jest 匹配器的使用
四. UI界面测试的原理
在编写页面的时候, 可能会设计到一些页面元素的测试, 最显而易见的就是UI组件库, 例如Element-UI
,Antd
肯定少不了对每一个组件进行单元测试,在前端工程化中, 代码都是运行在开发服务器的, 没有浏览器环境, 是如何进行DOM测试的呢?
原来, 在测试框架内部都集成了一些可以模拟浏览器环境的库, 例如jsDom
, 可以在里面模拟一些真实的DOM操作, 比如获取元素, 判断某个元素是否真实存在, 但是对于一些复杂的场景,jsDom
无法胜任, 对于这种就该无头浏览器
headless登场了, 例如: chromium
, 这就和真实浏览器一样,可以做更多更复杂的操作
五. 异步测试
异步在开发中无处不在, jest 测试异步代码