目录
一、Jest 是什么
二、Jest 开始使用步骤
1. 初始化一个新的项目
2. 安装
3. 创建测试文件并编写测试用例
4. 运行测试
三、单元测试框架基本原理
1. test
2. expect
3. toBe 匹配器
四、匹配器及适用范围
1. 普通匹配器
2. 与真假有关的匹配器
3. 数字
4. 字符串
5. 数组
6. 异常
7. 修饰符 not
五、测试套件
六、其他测试框架
一、Jest 是什么
Jest 是一个用于 JavaScript 应用程序的一种测试框架,是由 Facebook 开发并开源的,它为开发人员提供了一种简单而高效的方式来编写和运行各种类型的测试。
测试在我们技术中,意味着检查我们编写的代码是否满足某些期望,它是一种系统性的方法,用于发现和识别潜在的问题、错误或缺陷。
它提供了什么?
- 强大的断言库
- 内置的测试运行器
- 快照测试
- 模拟和覆盖率报告
- 异步测试支持
- 集成开发环境
二、Jest 开始使用步骤
1. 初始化一个新的项目
npm init
或
yarn init
2. 安装
npm install --save-dev jest
或
yarn add -dev jest
3. 创建测试文件并编写测试用例
要求在 .test.js 文件中编写测试用例,可以选择使用在一个文件中编写要测试的函数,也可以选择引用外部 .js 文件中要测试的函数。以下文章都基于在一个文件中编写。
一个简单的测试用例:
// index.test.js
function sum(a, b) {
return a + b;
}
test('sum 函数应该返回正确的结果', () => {
expect(sum(1, 2)).toBe(3);
})
4. 运行测试
在命令行中执行:
npm jest
或
yarn jest
如果测试用例成功,则会返回 Pass 结果:
三、单元测试框架基本原理
以上举了一个小例子,它包含了 test、expect、toBe 函数,这就引出了测试的基本框架:
1. test
test 是用于定义测试用例的主要函数。它接收两个参数:一个描述测试用例的字符串,一个包含测试代码的回调函数。
定义测试用例的函数还有 it,test 和 it 在功能和使用上没有实质性的区别。不过在语义上,test 函数用于描述各种类型的测试场景,而 it 函数则更加强调对单个单元进行测试的语义。
2. expect
expect 函数是 Jest 断言的起点,用于对被测代码的结果进行断言。它接受一个值作为参数,并返回一个包含各种断言方法的对象。通常的用法是将待测试的值放在 expect 函数的参数中,然后链式调用匹配器来对该值进行断言。
3. toBe 匹配器
toBe 是 Jest 的匹配器,用于比较 expect 函数中的值与断言中提供的期望值是否严格相等。匹配器是用于验证测试结果的工具,关于匹配器的更多知识请往下看。
四、匹配器及适用范围
匹配器是 Jest 中非常重要的概念,它可以提供很多种方式来编写断言,以验证测试结果。
1. 普通匹配器
1)toBe:
用于严格相等性检查,相当于 ===,比较值和引用是否相等。适用于原始数据类型的检查。【不适合复杂数据类型】
test('数字相等', () => {
const number = 1;
expect(number).toBe(1);
})
2)toEqual:
用于深度相等性检查,比较两个对象是否相等,它会递归地检查对象的所有属性和属性值,即使属性的顺序不一致,只要属性和属性值一样,就会匹配成功。适用于对象和数组等复杂数据类型的相等性检查。【也适用于原始数据类型】
test('数字相等', () => {
const number = 1;
expect(number).toEqual(1);
})
test('两个对象相等', () => {
const obj1 = { a: 1, b: { c: 2 } };
const obj2 = { b: { c: 2 }, a: 1 };
expect(obj1).toEqual(obj2);
})
2. 与真假有关的匹配器
1)toBeNull:
用于检查值是否为 null。
function getUser(id) {
if (id === 1) {
return { id: 1, name: 'Alice' }
} else {
return null;
}
}
test('getUser函数应该返回null', () => {
expect(getUser(3)).toBeNull();
})
2)toBeUndefined:
用于检查值是否为 undefined。
test('应该返回undefined', () => {
const obj = { id: 1, name: 'Alice' };
expect(obj.age).toBeUndefined();
})
3)toBeDefined:
用于检查值是否已定义,非 undefined,匹配 null 是通过的。
test('应该返回定义的匹配项', () => {
const obj = { id: 1, name: 'Alice', age: null };
expect(obj.name).toBeDefined();
expect(obj.age).toBeDefined();
})
4)toBeTruthy:
用于检查值是否为真值,即可以转换为 true 的值。
function isTrue(num) {
return num > 10;
}
test('应该返回真值', () => {
expect(isTrue(18)).toBeTruthy();
})
5)toBeFalsy:
用于检查值是否为假值,即可以转换为 false 的值。
function isTrue(num) {
return num > 10;
}
test('应该返回假值', () => {
expect(isTrue(9)).toBeFalsy();
})
3. 数字
1)toBeGreaterThan:
用于检查值是否大于给定的值。
test('5应该大于1', () => {
expect(5).toBeGreaterThan(3);
})
2)toBeLessThan:
用于检查值是否小于给定的值。
test('5应该小于7', () => {
expect(5).toBeLessThan(7);
})
3)toBeGreaterThanOrEqual:
用于检查值是否大于等于给定的值。
test('5应该大于等于5', () => {
expect(5).toBeGreaterThanOrEqual(5);
})
4)toBeLessThanOrEqual:
用于检查值是否小于等于给定的值。
test('5应该小于等于5', () => {
expect(5).toBeLessThanOrEqual(5);
})
5)toBeCloseTo:
用于检查浮点数之间的近似相等性,并且可以指定比较的精度。
注意以下几点:
- 默认精度:默认使用 4 位小数进行比较。如果实际值于期望值的差值小于等于 0.0001,则认为它们是近似相等的。
- 指定精度:可以通过提供第二个参数来指定比较的小数位数。
- 使用 delta:除了指定精度外,还可以提供第三个参数作为可选的 delta 值,delta 表示允许的差值范围,即实际值与期望值之间的差值小于等于 delta 时,将认为它们是近似相等的。
test('近似相等', () => {
const value = 1.23456789
expect(value).toBeCloseTo(1.23);
})
4. 字符串
toMatch:
用于检查字符串是否匹配某个特定项字符串,支持正则。适用于与模式匹配。
test('应该返回指定项', () => {
const string = 'Hello, World!';
expect(string).toMatch(/Hello/);
expect(string).toMatch('World');
});
5. 数组
toContain:
用于检查数组或字符串中是否包含特定的元素或子串。适用于检查是否存在。
test('应该返回指定项', () => {
const arr = [1, 2, 3, 4];
expect(arr).toContain(3);
});
6. 异常
toThrow:
用于检查函数是否抛出了异常,可以验证函数在执行的过程中是否会引发错误或抛出异常。toThrow 也可以接受一个参数,用于检查特定的异常类型和异常消息。
function isThrow(a, b) {
if (b === 0) {
throw new Error('error')
}
return a / b;
}
test('应该抛出异常', () => {
expect(() => isThrow(10, 0)).toThrow();
expect(() => isThrow(10, 0)).toThrow(Error);
expect(() => isThrow(10, 0)).toThrow('error');
})
7. 修饰符 not
可以使用 not 修饰符来否定匹配器的断言结果,进行相反的断言。在需要检查值不满足某些条件时非常有用。【可以放在所有匹配项前,用于否定】
test('not断言', () => {
const isTrue = 0;
const arr = [1, 2, 3];
expect(isTrue).not.toBeTruthy();
expect(arr).not.toContain(5);
})
为什么要传递一个函数作为参数呢?这是因为在断言函数是否抛出异常时,Jest 需要捕获并处理异常。如果直接将函数调用的结果作为参数传递给 expect,那么异常将会在断言之前被抛出,导致断言无法生效。所以,通过将函数作为参数传递给 expect,Jest 可以在执行断言之前捕获并处理函数抛出的异常,这样,Jest 就能够正确地判断异常是否符合预期,并作出相应的测试结果。
五、测试套件
Jest 提供了一个 describle 用于创建测试套件的全局函数。测试套件用于组织和描述相关的测试用例,并提供更清晰的测试结构和组织。
describle 函数接受两个参数:描述和回调函数。描述参数是一个字符串,用于描述测试套件的名称或目的,回调函数则包含了测试套件中的测试用例和其他相关代码。
describe('数学运算', () => {
test('应该返回正确的加运算', () => {
const result = 1 + 2;
expect(result).toBe(3);
});
test('应该返回正确的减运算', () => {
const result = 5 - 3;
expect(result).toBe(2);
});
...
});
六、其他测试框架
除 Jest 外,JavaScript 社区还有很多测试框架,比如 Mocha、Ava等,这些测试框架在功能和使用上有一些区别,主要体现在语法风格、断言库的选择、并发执行能力、易用性和扩展性等方面。