我在一个像这样的react组件中有一个api调用。
login = () => {
// <--- If I set the localStorage on this line the test passes.
apiRequest.then(res => {
localStorage.setItem('token', res.token);
});
}
为了测试它,我模拟了api调用。我想检查是否调用了本地存储,因此也要对localStorage进行模拟,但是,由于在模拟api调用中设置了localStorage,因此永远不会调用它。我的测试代码如下。有谁知道我如何检查模拟呼叫中是否设置了本地存储。我已经确认,如果将localStorage移到apiRequest之外,则可以正确模拟,问题肯定是它在apiRequest
中。
// This mocks out the api call
jest.mock('./api', () => {
return {
apiRequest: jest.fn(
() =>
new Promise(resolve => {
resolve();
})
),
};
});
const localStorageMock = (() => {
const store = {};
return {
setItem: jest.fn((key, value) => {
store[key] = value.toString();
})
}
})();
Object.defineProperty(window, 'localStorage', {
value: localStorageMock
});
it('sets a token in local storage', () => {
const { getByText } = render(<Login />);
const loginButton = getByText(/login/i);
// This passes
expect(apiRequest).toBeCalledTimes(1);
// This never gets called as it is being called in the apiRequest
expect(localStorage.setItem).toBeCalledWith('token', '1234');
});
如果不清楚,请告诉我,我将提供更多详细信息。
答案 0 :(得分:1)
localStorage.setItem
通过.then
login = () => {
apiRequest.then(res => {
localStorage.setItem('token', res.token);
});
}
因此,模拟对于异步流没有任何帮助。这小部分
.then(res => {
localStorage.setItem('token', res.token);
}
只是放在队列的末尾(如果您对详细信息感兴趣,则命名为microtask queue)
因此,只有在执行此小型微任务之后,您的测试代码才结束。
您如何处理?您可以在async way中编写测试,并将其他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');
});
关于await
的[UPD]有趣的事情是,它不仅可以与Promise一起使用,还可以与其他所有东西一起使用。它可以像Promise.resolve(<some value here>)
一样工作。因此,就您而言
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');
});
也可以。但是我相信它看起来令人困惑(“ waaaat?.toHaveBeenCalled()
返回Promise真实吗?!”)和可疑(这是一种魔术!我不能碰它!)。因此,最好选择一些具有直接“延迟”功能的版本
答案 1 :(得分:0)
当您尝试测试异步代码时,一个常见的问题是您还需要进行异步测试,尝试将await
到apiRequest
进行解析,然后验证是否调用了本地存储。 / p>