主题
单元测试
一. 单元测试的作用与好处
对于一个完整的前端工程来讲, 有单元测试环节是非常有必要的, 特别当你开发的是一个开源库或者插件, 拥有完整的单元测试会让使用者心里有底, 使用起来会更放心, 在同等功能的库面前, 拥有单元测试的库更受欢迎
谁也不能保证自己写的代码里面没有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
+2mocha + 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 测试异步代码
