一,测试分类
1.1 E2E测试(end to end端到端测试)
属于黑盒测试。
主要通过测试框架,站在用户测试人员的角度,模拟用户的操作进行页面功能的验证,不管内部实现机制,完全模拟浏览器的行为。(但对于前端开发,E2E测试用的不多,通常都是做单元测试)
常用的框架有 Puppeteer、Cypress、Playwright、Selenium 、cucumber、TestCafe等
1.2 单元测试
属于白盒测试。
单元测试是最小可测试单元,针对方法、函数、类等单个功能。他主要是为了测试自己写的代码,包括各种函数、组件、模块等。
常用的框架有 Jest、Jasmine、Mocha等
单元测试的优势:
代替了测试的一部分工作,减少手动测试时间;
保证了代码的各个部分能够独立、正确的工作;
提高了代码的质量和可维护性;
减少了迭代新特性时产生的Bug;
有利于重构。
1.3 集成测试
集成测试主要应用在耦合度高的函数或组件。他的目的主要是测试经过单元测试后的各个模块组合在一起作为一个整体是否能正常工作。
常用的框架有Vue-Test-Utils 、ReactTestUtils、Enzyme、React-Testing-Library等
二,单元测试详解
2.1 主流单元测试框架对比
Mocha:生态最好,应用最多,但是需要很多配置来实现他的高扩展性。
Jasmine:单元测试框架的“元老”,开箱即用,但是异步测试支持较差。
Jest:基于Jasmine,做了大量修改并添加了很多特性,同样开箱即用,但异步测试支持良好。语法与Mocha也比较像。
后续均以Jest为例,详细的学习可以查看官网
2.2 项目环境配置
安装依赖:npm install --save-dev jest
增加启动命令(package.json文件):"test": "jest" // 即npm run test即可执行
jest配置文件:jest.config.js文件的相关配置 // 对于jest的一些基本配置,可参考官网配置
babel配置文件:babel.config.js // jest执行会将将文件交给babel处理,因此也需要配置
2.3 常用语法
2.3.1 Expect(断言)
概念:expect通常都是配合匹配器一起使用,结合不同的匹配器去断言不同类型的值。
应用:
expect(value) - 断言value值满足一定条件,后面链式调用匹配器 (eg:expect(value).toBe(4); )
2.3.2 匹配器
概念:jest的匹配器是核心语法,他的机制可以让我们使用各种方法进行测试。其中包含数字、字符串、数组、对象等。
分类:
数值:
- .toBe(number) - 判断是否严格相等。expect(value).toBe(4);
- .toEqual(number) - 判断值是否相等
- .toBeCloseTo(number) - 判断浮点数是否相等
- .toBeLessThan(number) - 判断是否小于期望值
- .toBeGreaterThan(number) - 判断是否大于期望值
- .toBeLessThanOrEqual(number) - 判断是否小于等于期望值
- .toBeGreaterThanOrEqual(number) - 判断是否大于等于期望值
- .toBeNaN(number)
- ... ...
真假值:
- .toBeNull() - 判断是否为null
- .toBeDefined() - 判断是否被定义
- .toBeUndefined() - 判断是否未定义(与上面相反)
- .toBeTruthy() - 判断是否为真值(true、非0数字、非空字符串、对象/数组等)
- .toBeFalsy() - 判断是否为假值(false、0、空字符串、null、undefined等)
- ... ...
字符串:
- .toMatch(str) - 可以检查对具有toMatch正则表达式的字符串
eg:expect('Christoph').toMatch(/stop/);
数组/可迭代对象:
- .toContain(value) - 判断数组或者对象是否包含value值
取反:
- .not - 取反 (eg:expect(value).not.toBe(4);)
异常:
- .toThrow(error?) - 判断是否抛出了一个异常(参数可以为Error对象、抛出异常的文本)
eg:function codeSth() {
throw new Error('param is wrong!');
}
test('compiling codeSth as expected', () => {
expect(() => codeSth()).toThrow();
expect(() => codeSth()).toThrow(Error);expect(() => codeSth()).toThrow(/^param is wrong!$/);
});
- ... ...更多匹配器可查看官方API
2.3.3 异步代码测试
解读:测试一段异步代码,返回一个Promise时,Jest会等待Promise的resove状态,如果Promise的状态变为rejected,,测试将会失败。
应用:
1,通过名为fetchData的promise对象来进行断言
2,通过async、await、resolves、rejects等互相配合链式调用进行断言
应用示例:
1,通过fetchData()断言
// 假设fetchData()这个promise成功后会返回"operation success"字符串,则断言
test('the data is operation success', () => {
return fetchData().then((data) => {
expect(data).toBe('operation success');
});
});
2,通过async、await、resolves、rejects配合链式调用进行断言
// 将test的回调设置成异步,通过await等待断言fetchData返回的promise值链式调用期望值
test('the data is operation success', async () => {
return fetchData().then((data) => {
await expect(fetchData()).resolves().toBe('operation success');
});
});
// 断言异步函数测试失败,返回了error
test('the data operation with an error', async () => {
await expect(fetchData()).rejects.toMatch('error');
});
2.4 生命周期钩子
- test(name, func [, timeout]) - 运行测试的方法(别名:it(name, func [, timeout]))
- beforeAll(func [, timeout]) - 文件内 所有测试开始前执行的钩子函数
- afterAll(func [, timeout]) - 文件内 所有测试完成后执行的钩子函数
- beforeEach(func [, timeout]) - 文件内 每个测试开始前执行的钩子函数
- afterEach(func [, timeout]) - 文件内 每个测试完成后执行的钩子函数
- describe(name, func) - 将多个test测试组合到一起,成为一个块,每个块中都拥有自己的一套生命周期钩子,就像vue的子组件。
示例:
beforeAll(() => {
console.log('全局beforeAll');
});
afterAll(() => {
console.log('全局afterAll');
});
describe('test truthy and falsy', () => {
beforeAll(() => {
console.log('局部beforeAll');
});
afterAll(() => {
console.log('局部afterAll');
});
test('is truthy', () => {
expect(1).toBeTruthy();
});
test('is falsy', () => {
expect(0).toBeFalsy();
});
});
三,官网地址
细节的编写问题可查看官方API:
全局设定 · JestJest会将这些方法和对象注入到测试文件的全局环境里, 所以你在使用的时候不再需要进行require或者import。 如果你习惯编写明确的导入,你可以在测试文件顶部添加 import {describe, expect, test} from '@jest/globals'。https://jestjs.io/zh-Hans/docs/api