1. 异步测试哪里特殊?
在JavaScript中执行异步代码是很常见的。当你有以异步方式运行的代码时,Jest 需要知道当前它测试的代码是否已完成,然后它可以转移到另一个测试。
经典错误:
默认情况下,Jest 测试一旦执行到末尾就会完成。问题在于一旦 fetchData 执行结束,此测试就在没有调用回调函数前结束。
function fetchData(callback) {
setTimeout(() => callback('peanut butter'), 5 * 1000);
}
// 经典错误
test('the data is peanut butter', () => {
function callback(data) {
expect(data).toBe('peanut butter');
}
fetchData(callback);
});
2. 异步测试基本模式
2.1. test('...', (done) => {...}) 模式
- Jest will wait if you provide an argument to the test function, usually called done.
- 默认测试超时时间:5s
- 可通过 testTimeout 选项配置
- 默认测试超时时间:5s
function fetchData(callback) {
setTimeout(() => callback('peanut butter'), 5 * 1000);
}
test('the data is peanut butter', (done) => {
function callback(data) {
expect(data).toBe('peanut butter');
done();
}
fetchData(callback);
});
2.2. Promise 模式
- If a promise is returned from test, Jest will wait for the promise to resolve before letting the test complete.
function fetchData() {
return new Promise((resolve) => {
setTimeout(() => resolve('peanut butter'), 5 * 1000);
});
}
test('the data is peanut butter', () => {
return fetchData().then((data)=>{
expect(data).toBe('peanut butter');
});
});
2.3. Async|Await 模式
- Alternatively, you can use async and await in your tests.
function fetchData() {
return new Promise((resolve) => {
setTimeout(() => resolve('peanut butter'), 5 * 1000);
});
}
test('the data is peanut butter', async () => {
const data = await fetchData();
expect(data).toBe('peanut butter');
});
3. expect.hasAssertions()、expect.assertions(number)
观察一个例子:
代码语言:javascript复制function fetchData() {
return new Promise((resolve) => {
setTimeout(() => resolve('peanut butter'), 5 * 1000);
});
}
test('the data is peanut butter', async () => {
try {
await fetchData();
} catch (e) {
expect(e).toMatch('error');
}
});
这个例子中,显然,代码不会运行到 "expect(e).toMatch('error')",但测试用例还是通过了,这显然不是我们想要的。
- expect.hasAssertions()
- expect.hasAssertions() verifies that at least one assertion is called during a test. This is often useful when testing asynchronous code, in order to make sure that assertions in a callback actually got called.
- expect.assertions(number)
- expect.assertions(number) verifies that a certain number of assertions are called during a test. This is often useful when testing asynchronous code, in order to make sure that assertions in a callback actually got called.
修改一下例子:
代码语言:javascript复制function fetchData() {
return new Promise((resolve) => {
setTimeout(() => resolve('peanut butter'), 5 * 1000);
});
}
test('the data is peanut butter', async () => {
expect.hasAssertions(); // 划重点
try {
await fetchData();
} catch (e) {
expect(e).toMatch('error');
}
});
再观察一个例子:
代码语言:javascript复制function doAsync(callback1, callback2) {
callback1(true);
}
test('the data is peanut butter', async () => {
expect.assertions(2); // 划重点
function callback1(data) {
expect(data).toBeTruthy();
}
function callback2(data) {
expect(data).toBeTruthy();
}
doAsync(callback1, callback2);
});
4. .resolves、.rejects
- Use resolves to unwrap the value of a fulfilled promise so any other matcher can be chained. If the promise is rejected the assertion fails.
- Use .rejects to unwrap the reason of a rejected promise so any other matcher can be chained. If the promise is fulfilled the assertion fails.
示例1:
代码语言:javascript复制function fetchData() {
return new Promise((resolve) => {
setTimeout(() => resolve('peanut butter'), 6 * 1000);
});
}
test('the data is peanut butter', () => {
return expect(fetchData()).resolves.toBe('peanut butter');
});
示例2:
代码语言:javascript复制function fetchData() {
return new Promise((resolve, reject) => {
setTimeout(() => {
reject(new Error('peanut butter'));
}, 3 * 1000);
});
}
test('the data is peanut butter', async () => {
return expect(fetchData()).rejects.toThrow("peanut butter");
});
示例3:
代码语言:javascript复制function fetchData() {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(1);
}, 3 * 1000);
}).then(()=>{
throw new Error('peanut butter');
});
}
test('the data is peanut butter', () => {
return expect(fetchData()).rejects.toThrow("peanut butter");
});
参考:
Jest Matchers: https://jestjs.io/docs/en/using-matchers Jest Expect API: https://jestjs.io/docs/zh-Hans/expect An Async Example: https://jestjs.io/docs/en/tutorial-async