Javascript Jest-在模拟的异步函数中调用检查本地存储
我在react组件中有一个api调用,如下所示Javascript Jest-在模拟的异步函数中调用检查本地存储,javascript,reactjs,jestjs,Javascript,Reactjs,Jestjs,我在react组件中有一个api调用,如下所示 login = () => { // <--- If I set the localStorage on this line the test passes. apiRequest.then(res => { localStorage.setItem('token', res.token); }); } 如果有什么不清楚的地方,请告诉我,我会提供更多细节。当您尝试测试异步代码时,一个常见的问题是您还
login = () => {
// <--- If I set the localStorage on this line the test passes.
apiRequest.then(res => {
localStorage.setItem('token', res.token);
});
}
如果有什么不清楚的地方,请告诉我,我会提供更多细节。当您尝试测试异步代码时,一个常见的问题是您还需要异步测试,尝试
等待到要解决的apiRequest
,然后验证是否调用了本地存储。本地存储。通过异步调用setItem
。然后
login = () => {
apiRequest.then(res => {
localStorage.setItem('token', res.token);
});
}
因此,模拟对异步流没有任何帮助。这一小部分
.then(res => {
localStorage.setItem('token', res.token);
}
仅放在队列的末尾(如果您对详细信息感兴趣,则命名为)
因此,您的测试代码已经完成,并且只有在这个小的微任务被执行之后
你怎么处理?您可以在中编写测试,并将附加的expect
放入专用的微任务中,该微任务将在使用localStorage.setItem
调用后运行
您可以为此使用setTimeout
(宏任务):
it('sets a token in local storage', done => {
const { getByText } = renderLogin();
const loginButton = getByText(/login/i);
expect(apiRequest).toBeCalledTimes(1);
setTimeout(() => {
// runs after then(....setItem) has been called
expect(localStorage.setItem).toBeCalledWith('token');
done();
}, 0);
});
或者使用Promise/async/await创建微任务:
it('sets a token in local storage', async () => {
const { getByText } = renderLogin();
const loginButton = getByText(/login/i);
expect(apiRequest).toBeCalledTimes(1);
await Promise.resolve(); // everything below goes into separate microtask
expect(localStorage.setItem).toBeCalledWith('token');
});
[UPD]关于wait
的有趣之处在于,它不仅可以用于承诺,还可以用于其他任何东西。它可以像Promise.resolve()
一样工作。那么你的情况呢
it('sets a token in local storage', async () => {
const { getByText } = renderLogin();
const loginButton = getByText(/login/i);
await expect(apiRequest).toBeCalledTimes(1);
expect(localStorage.setItem).toBeCalledWith('token');
});
这也行。但我相信它看起来令人困惑(“waaat?does.tohavebeencall()
return Promise for real?!”)和可疑(这是一个魔术!我不允许碰它!)。因此,最好选择一些直接“延迟”的版本,实际上localStorage.setItem
在render()
方法中调用,我认为它是renderLogin()
呈现的apiRequest
逻辑的一部分。所以叫它。但是由于localStorage.setItem
是以异步方式调用的,因此您无法立即检查它。感谢@skyboyer的响应。你是什么意思?我已经更新了问题,可能是在你发布之后。更新是:我已经确认,如果我将localStorage移到apiRequest之外,它就可以工作,因此它被正确地模拟了,问题肯定是它在apiRequest中。谢谢Miguel,但我不认为是这样,因为api调用被模拟了,它实际上不会触发真正的api调用,但是我在问题中提供的代码片段中定义的那个。我刚刚尝试添加了async
和wait
,结果是一样的。感谢@skyboyer,它仍然没有通过。虽然当我删除mock-apirest时,它似乎也不起作用,所以我现在不确定它是mock还是异步性质。你们似乎相当确定这是由于异步造成的?是的,对我来说,这看起来是一个非常简单的问题。也许我错过了什么。您能在codesandbox中提供小而完整的代码片段吗?它需要一些时间才能在没有挂钩的旧React下工作(因为codesandbox抛出一个错误“jest未定义”)。首先,在测试中,您正在检查'/sessions'
,但实际上调用了'/login'
。接下来,您的模拟api调用返回Promise.resolve()
,但它应该返回带有{data:{token:…}}}
的对象,否则它将在
中抛出错误,因为response.data.token未定义
否,我们仍在努力解决setItem
由于某些原因未被识别为jest mock的问题。
it('sets a token in local storage', async () => {
const { getByText } = renderLogin();
const loginButton = getByText(/login/i);
await expect(apiRequest).toBeCalledTimes(1);
expect(localStorage.setItem).toBeCalledWith('token');
});