这是我参与更文挑战的第 21 天,活动详情查看:更文挑战
Jest是Facebook团队构建和维护的JavaScript测试框架,基于Jasmine,是一款优雅、简洁的 Javascript 测试框架。
简单使用
先创建一个项目,并安装 jest。
yarn init
yarn add --dev jest
编写一个模块代码和相应的测试用例。
// sum.js
function sum(a, b) {
return a + b;
}
module.exports = sum;
// sum.test.js 相应的模块测试文件命名为 .test.js
const sum = require('./sum');
test('adds 1 + 2 to equal 3', () => {
expect(sum(1, 2)).toBe(3); // 断言
});
test 块就是我们的一个测试用例,是测试的最小单位,第一个参数是描述信息,第二个参数是实际执行的函数。在test块的执行函数内,存在断言expect(sum(1, 2)).toBe(3);。所谓断言,是判断源码的实际执行结果与预期结果是否一致,如果不一致就抛出一个错误。一个测试用例里可以有很多断言。
expect(...)返回一个“期望”的值,toBe是相应匹配规则的匹配器。
expect(sum(1,2)) 得到 sum(1.2) 的返回值,根据匹配器 toBe 的规则与所传递的值进行比较,如果符合匹配规则就通过测试,否则抛出错误。
运行命令查看测试结果:
yarn test
匹配器
上面我们使用了 toBe 匹配器,它本质是使用 Object.js 方法对值进行比较。Jest 提供了各种功能的匹配器,详细可以翻看API 文档。
我们来了解一下常用的匹配器。
相反的匹配
刚刚提到了toBe 用于判断值是否与预期相等,如果要判断值不等于预期值,可以使用not.toBe(...),如下所示:
test('1 + 1 不等于 3', () => {
expect(1+1).not.toBe(3);
})
匹配对象
如果要检查对象,不能用toBe,用toEqual,它会递归检查对象或数组的每个字段。它也可以用来匹配其它类型的值。
test('对象比较', () => {
const data = { one: 1 };
data['two'] = 2;
expect(data).toEqual({ one: 1, two: 2 });
});
test('toEqual 匹配各个类型值', () => {
expect(1 + 1).toEqual(2);
expect('test').toEqual('test');
expect(true).toEqual(true);
expect(undefined).toEqual(undefined);
})
真值匹配器
Jest 具备以下真值匹配器:
toBeNull只匹配nulltoBeUndefined只匹配undefinedtoBeDefined与toBeUndefined相反toBeTruthy匹配任何if语句为真toBeFalsy匹配任何if语句为假
如下所示:
test('用 null 进行测试', () => {
const n = null; // ✔
expect(n).toBeNull(); // ✔
expect(n).toBeDefined(); // ✔
expect(n).not.toBeUndefined(); // ✔
expect(n).not.toBeTruthy(); // ✔
expect(n).toBeFalsy(); // ✔
});
数字匹配器
Jest 也提供了便于判断 number 类型的匹配器,如下所示:
test('two plus two', () => {
const value = 2 + 2;
expect(value).toBeGreaterThan(3);
expect(value).toBeGreaterThanOrEqual(3.5);
expect(value).toBeLessThan(5);
expect(value).toBeLessThanOrEqual(4.5);
// toBe 跟 toEqual 对 number 类型来说是一致的
expect(value).toBe(4);
expect(value).toEqual(4);
});
字符串匹配器
字符串匹配器允许使用正则表达式进行匹配,如下所示:
test('单词中没有字母 I', () => {
expect('team').not.toMatch(/I/);
});
test('单词中存在字符 "stop"', () => {
expect('Christoph').toMatch(/stop/);
});
数组及可迭代对象
对于数组和可迭代对象,我们可以使用匹配器toContain匹配其中是否存在某个项,如下所示:
const shoppingList = [
'diapers',
'kleenex',
'trash bags',
'paper towels',
'milk',
];
test('购物列表中存在 milk', () => {
expect(shoppingList).toContain('milk');
expect(new Set(shoppingList)).toContain('milk');
});
异常匹配器
Jest 还提供了Error相关的匹配器,如下所示:
function compileAndroidCode() {
throw new Error('you are using the wrong JDK');
}
test('compiling android goes as expected', () => {
expect(() => compileAndroidCode()).toThrow();
expect(() => compileAndroidCode()).toThrow(Error);
// 也可以用字符串或正则匹配错误提示语
expect(() => compileAndroidCode()).toThrow('you are using the wrong JDK');
expect(() => compileAndroidCode()).toThrow(/JDK/);
});
以上是常用的一些匹配器的介绍,更多Jest匹配器可以查询API 文档。
异步代码测试
先来做一个回调函数的用例测试如下:
test('测试异步代码', () => {
function syncFunc(cb) {
setTimeout(() => {
cb(1+2); // 得到的值是 3,不能通过下面的断言
}, 1000)
}
function callBack(data) {
expect(data).toBe(2); // 只有为 2 时才通过测试
}
syncFunc(callBack);
})
运行会发现测试通过,但是报错了。这是为什么呢?
这是因为Jest默认情况下,代码执行一旦到达执行上下文底部,测试立即结束,不会等待回调函数的执行。
解决方案是使用单个参数调用 done。 Jest会等done回调函数执行结束后,结束测试。如下所示:
test('测试异步代码', (done) => {
function syncFunc(cb) {
setTimeout(() => {
cb(1 + 2);
}, 1000)
}
function callBack(data) {
try {
expect(data).toBe(3);
done();
} catch(error) {
done(error);
}
}
syncFunc(callBack);
})
其中,若 expect 执行失败,它会抛出一个错误,后面的 done() 不再执行,如果 done() 函数从未被调用,测试用例会执行失败(显示超时错误)。
若我们想知道测试用例为何失败,我们必须将 expect 放入 try 中,将 error 传递给 catch 中的 done函数。 否则,最后控制台将显示一个超时错误失败,不能显示我们在 expect(data) 中接收的值。
如果是使用 promise,需要 return该promise,这样 Jest 会等待它执行完毕,如下所示:
test('测试 Promise resolve 执行情况', () => {
return Promise.resolve(3).then((data) => {
expect(data).toBe(3);
})
})
如果期望Promise被Reject,则需要使用.catch 方法。
test('测试 Promise reject 执行情况', () => {
expect.assertions(1); // 如果不加,Promise.resolve(3) 也能通过测试
return Promise.reject(3).catch((data) => {
expect(data).toBe(3);
})
})
其中,添加了 expect.assertions 来验证一定数量的断言被调用。 否则,一个fulfilled状态的Promise不会让测试用例失败。
另外,Jest 还提供了.resolves 和 .rejects 匹配器,使用如下:
test('测试 Promise 执行情况', () => {
return expect(Promise.resolve(5)).resolves.toBe(5);
})
test('测试 Promise 执行情况', () => {
return expect(Promise.reject(5)).rejects.toBe(5);
})
另外,我们也可以使用 async 和 await,让异步代码同步执行。可以在传递给test的函数前面加上async,如下:
test('测试异步执行情况', async () => {
let data = await Promise.resolve(3)
expect(data).toBe(3);
})
test('测试 Promise 执行情况', async () => {
await expect(Promise.resolve(5)).resolves.toBe(5);
})
总结
- 异步代码的测试实现主要是通过让
Jest等待异步代码执行完毕,不要立即结束测试。
如果文章对你有帮助,点个赞呗~