这是我参与更文挑战的第 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
只匹配null
toBeUndefined
只匹配undefined
toBeDefined
与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
等待异步代码执行完毕,不要立即结束测试。
如果文章对你有帮助,点个赞呗~