Jest的文档有时候晦涩难懂,中文文档又有股浓浓的机翻味,因此将自己的学习笔记整理出来,融入自己的一些理解,力争做到通俗易懂。后面还会整理函数模拟,计时器模拟等内容,希望对大家有所帮助。
回调函数
需在回调函数中调用done
函数,Jest会在done
函数执行结束后,结束测试。
function fetchData(fn) {
setTimeout(() => {
fn('Hello world')
}, 1000);
}
test('callback', (done) => {
function callback(data) {
expect(data).toBe('Hello world')
done() // 在回调函数中记得调用done函数,不然测试用例将无法通过,显示超时错误。
}
fetchData(callback)
})
复制代码
如果done()
函数从未被调用,测试用例会显示超时错误!!!
下面修改一下测试用例,将expect(data).toBe('Hello world')
改为 expect(data).toBe('Hello Jest')
。
test('callback', (done) => {
function callback(data) {
expect(data).toBe('Hello Jest')
done()
}
fetchData(callback)
})
复制代码
显然这是不能通过测试的,但是Jest给我们的提示却是thrown: "Exceeded timeout of 5000 ms for a test……
,即超时错误。这是因为expect
执行失败后,会抛出一个错误,导致后面的 done()
不再执行,从而显示超时。如果我们想知道测试用例失败的原因,需要将 expect
放入 try
中,然后将 error
传递给 catch
中的 done
函数。
因此,完善的测试用例应该如下:
test('callback', done => {
function callback(data) {
try {
expect(data).toBe('Hello Jest')
done()
} catch (error) {
done(error)
}
}
fetchData(callback)
});
复制代码
Promise
如果异步代码使用了 Promise,需要在测试用例中将Promise对象返回
function fetchData() {
return new Promise((resolve) => {
setTimeout(() => {
resolve('Hello world')
}, 1000);
})
}
test('Promise', () => {
// 一定记得加上 return,否则测试在 fetchData 执行完成之前就已经结束,随后then中的expect也不会执行,会导致超时错误!!!
return fetchData().then(data => {
expect(data).toBe('Hello world')
})
})
复制代码
如果需要测试reject
状态的Promise,除了需要在catch
方法中执行断言外,还需要使用expect.assertions
来验证是否调用了指定次数的断言。
比如,我们需要测试接口失败时返回的数据。先修改fetchData
函数,通过传入的success
参数来模拟接口的成功和失败。
function fetchData(success) {
return success ? Promise.resolve('success message') : Promise.reject('error message')
}
复制代码
如果接口返回失败的情况下,此时以下的测试用例时可以通过的,这没有问题。
test('Promise rejected', () => {
return fetchData(false).catch(error => {
expect(error).toBe('error message') // 测试通过
})
})
复制代码
但是如果接口返回成功,其实测试用例也是可以通过的。这是因为接口成功的话,是不会执行catch
函数的。那么 catch
里面的断言也就不会被执行,所以能通过测试。看下面的例子:
test('Promise rejected', () => {
// 接口成功,因为不会执行catch
return fetchData(true).catch(error => {
expect(error).toBe('error message') // 测试通过
})
})
复制代码
可以这并不符合我们的预期啊,我们就是想测试接口失败时的数据,此时,我们可以使用expect.assertions(1)
,这样我们就可以知道,至少有一次expect
被执行,也就是catch
一定会被执行,那么,fetchData
一定要是返回失败的,而不是成功的,因此我们要将fetchData(true)
要改为fetchData(false)
,来确保接口是返回是失败的,这样才是严谨的测试用例。
test('Promise rejected', () => {
expect.assertions(1) // 至少执行一次 expect
// 需要将fetchData(true)改为fetchData(false),接口失败才能执行catch里面的断言
return fetchData(false).catch(error => {
expect(error).toBe('error message') // 测试通过,
})
})
复制代码
当测试 promise 的时候,特别是测试 catch,一定要加 expect.assertions() 确保测试代码会执行
resolves 和 rejects 匹配器
resolves
和rejects
匹配器可以让我们很方便的测试Promise。当然不要忘记将整个断言作为返回值返回
test('the data is success message', () => {
// 不要忘记将整个断言作为返回值返回
return expect(fetchData(true)).resolves.toBe('success message')
});
test('the fetch fails with an error', () => {
// 不要忘记将整个断言作为返回值返回
return expect(fetchData(false)).resolves.toBe('error message')
});
复制代码
async 和 await
我们也可以在测试中使用async
和 await
,写异步测试用例时,可以在传递给test
的函数前面加上async
。
test('the data is success message', async () => {
const data = await fetchData(true)
expect(data).toBe('success message')
});
test('the fetch fails with an error', async () => {
expect.assertions(1)
try {
await fetchData(false)
} catch (e) {
expect(e).toBe('error message')
}
});
复制代码
你也可以将 async
and await
和 resolves
or rejects
一起使用。
test('the data is success massage', async () => {
await expect(fetchData(true)).resolves.toBe('success message');
});
test('the fetch fails with an error', async () => {
await expect(fetchData(false)).rejects.toBe('error message');
});
复制代码