Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/javascript/442.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Javascript Jest-在模拟的异步函数中调用检查本地存储_Javascript_Reactjs_Jestjs - Fatal编程技术网

Javascript Jest-在模拟的异步函数中调用检查本地存储

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); }); } 如果有什么不清楚的地方,请告诉我,我会提供更多细节。当您尝试测试异步代码时,一个常见的问题是您还

我在react组件中有一个api调用,如下所示

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');
});