gpt4 book ai didi

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

转载 作者:行者123 更新时间:2023-11-29 23:12:06 26 4
gpt4 key购买 nike

我在 React 组件中有一个 api 调用,看起来像这样。

login = () => {
// <--- If I set the localStorage on this line the test passes.
apiRequest.then(res => {
localStorage.setItem('token', res.token);
});
}

为了测试它,我模拟了 api 调用。我想检查是否调用了本地存储,因此也模拟了 localStorage,但是,由于 localStorage 是在模拟的 api 调用中设置的,因此它永远不会被调用。我的测试代码如下。有谁知道我如何检查模拟调用中是否设置了本地存储。我已经确认,如果我将 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');
});

如果有任何不清楚的地方,请告诉我,我会提供更多详细信息。

最佳答案

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

[UPD] 关于 await 的有趣事情它可以与其他所有东西一起使用,而不仅仅是 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 吗?!”)和可疑(这是一个魔法!我不能碰它!)。所以最好选择一些直接“延迟”的版本

关于javascript - Jest - 在被模拟的异步函数中调用检查本地存储,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53714884/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com