使用 React Hooks 测试库测试轮询
Testing Polling with React Hooks Testing Library
我已经创建了一个名为 usePolling 的自定义挂钩,它接受一个函数来轮询一段时间(由间隔声明)挂钩工作正常但现在我正在尝试测试它......我收到警告:测试中 TestHook 的更新未包含在 act(...) 中。错误,我看了 Kent 关于测试异步挂钩的视频,但是我正在努力让警告消失......但是测试通过了。
jest 抱怨的台词是
} finally {
> 67 | setBusy(false);
| ^
68 | }
这是有道理的,因为这是一个状态变化......但是你会看到我的测试已经包含了所有内容
这是我制作的 GIST > https://gist.github.com/FrancisLeigh/f62bb3c68d16e434019d4843c86e6cf6
提前致谢:slight_smile:
我设法通过接受一个 axiosOverride
道具来使钩子在测试时更加可靠,然后我显然可以更轻松地控制和断言。
任何感兴趣的人 > https://gist.github.com/FrancisLeigh/f62bb3c68d16e434019d4843c86e6cf6
抱歉回复晚了,但我只是设法检查了你的答案,我发现上面的解决方案有点老套,因为它强制创建一个 prop 来覆盖 axios,所以我在下面详细说明了你的问题的可能解决方案.我没有根据您在下面发布的 git link 进行此操作,因为看起来可能会造成混乱。
我们使用 await waitFor 来确保我们可以在调用 promise 之前和之后侦听对 busy 所做的更新非常重要,这样它就不会触发 Warning: An update to TestHook inside a test was not wrapped in act(...)
警告。
我认为这个消息有点欺骗用户,因为用户通常认为将所述问题包装在一个行为中会解决问题,但在这种情况下,问题实际上在于使用 setInterval 并且它能够更新状态。
希望我的解决方案对您有所帮助,并努力弃用 axiosOverrideProp
。
usePollingHook.js
import * as React from 'react';
import requestAdaptor from './requestAdaptor';
export function usePollingHook(pollTimeMs = 2000) {
const [busy, setBusy] = React.useState(false);
const [isPolling, setPolling] = React.useState(false);
const [data, setData] = React.useState(null);
React.useEffect(() => {
if (isPolling) {
const poll = async () => {
if (!busy) {
setBusy(true);
const resp = await requestAdaptor();
setBusy(false);
setData(resp);
}
};
const interval = setInterval(poll, pollTimeMs);
return () => clearInterval(interval);
}
return () => null;
}, [isPolling, busy, setBusy, pollTimeMs]);
return {
data,
busy,
isPolling,
setPolling,
};
}
index.spec.js
import { act, renderHook } from '@testing-library/react-hooks';
import { usePollingHook } from './usePollingHook';
import requestAdaptor from './requestAdaptor';
jest.useFakeTimers();
jest.mock('./requestAdaptor', () => jest.fn().mockResolvedValue(1));
describe('usePollingHook', () => {
it('returns default props', () => {
const { result } = renderHook(() => usePollingHook());
expect(result.current.data).toEqual(null);
expect(result.current.busy).toEqual(false);
expect(result.current.isPolling).toEqual(false);
expect(result.current.setPolling).toEqual(expect.any(Function));
});
it('ensures the state is updated when the hook has polled a request', async (done) => {
const { result, waitFor } = renderHook(() => usePollingHook());
expect(result.current.busy).toEqual(false);
act(() => {
result.current.setPolling(true);
});
await waitFor(() => expect(result.current.isPolling).toEqual(true));
act(() => {
jest.advanceTimersByTime(10000);
});
await waitFor(() => expect(result.current.busy).toEqual(true));
expect(requestAdaptor).toBeCalled();
await waitFor(() => expect(result.current.busy).toEqual(false));
expect(result.current.data).toEqual(1);
done();
});
});
requestAdaptor.js
一些伪造的 http 函数会环绕一个承诺,例如模拟 axios 或 fetch
export function requestAdaptor() {
return new Promise((resolve) => {
return setTimeout(() => {
return resolve({ userId: 1 });
}, 1000);
});
}
export default requestAdaptor;
我已经创建了一个名为 usePolling 的自定义挂钩,它接受一个函数来轮询一段时间(由间隔声明)挂钩工作正常但现在我正在尝试测试它......我收到警告:测试中 TestHook 的更新未包含在 act(...) 中。错误,我看了 Kent 关于测试异步挂钩的视频,但是我正在努力让警告消失......但是测试通过了。
jest 抱怨的台词是
} finally {
> 67 | setBusy(false);
| ^
68 | }
这是有道理的,因为这是一个状态变化......但是你会看到我的测试已经包含了所有内容
这是我制作的 GIST > https://gist.github.com/FrancisLeigh/f62bb3c68d16e434019d4843c86e6cf6
提前致谢:slight_smile:
我设法通过接受一个 axiosOverride
道具来使钩子在测试时更加可靠,然后我显然可以更轻松地控制和断言。
任何感兴趣的人 > https://gist.github.com/FrancisLeigh/f62bb3c68d16e434019d4843c86e6cf6
抱歉回复晚了,但我只是设法检查了你的答案,我发现上面的解决方案有点老套,因为它强制创建一个 prop 来覆盖 axios,所以我在下面详细说明了你的问题的可能解决方案.我没有根据您在下面发布的 git link 进行此操作,因为看起来可能会造成混乱。
我们使用 await waitFor 来确保我们可以在调用 promise 之前和之后侦听对 busy 所做的更新非常重要,这样它就不会触发 Warning: An update to TestHook inside a test was not wrapped in act(...)
警告。
我认为这个消息有点欺骗用户,因为用户通常认为将所述问题包装在一个行为中会解决问题,但在这种情况下,问题实际上在于使用 setInterval 并且它能够更新状态。
希望我的解决方案对您有所帮助,并努力弃用 axiosOverrideProp
。
usePollingHook.js
import * as React from 'react';
import requestAdaptor from './requestAdaptor';
export function usePollingHook(pollTimeMs = 2000) {
const [busy, setBusy] = React.useState(false);
const [isPolling, setPolling] = React.useState(false);
const [data, setData] = React.useState(null);
React.useEffect(() => {
if (isPolling) {
const poll = async () => {
if (!busy) {
setBusy(true);
const resp = await requestAdaptor();
setBusy(false);
setData(resp);
}
};
const interval = setInterval(poll, pollTimeMs);
return () => clearInterval(interval);
}
return () => null;
}, [isPolling, busy, setBusy, pollTimeMs]);
return {
data,
busy,
isPolling,
setPolling,
};
}
index.spec.js
import { act, renderHook } from '@testing-library/react-hooks';
import { usePollingHook } from './usePollingHook';
import requestAdaptor from './requestAdaptor';
jest.useFakeTimers();
jest.mock('./requestAdaptor', () => jest.fn().mockResolvedValue(1));
describe('usePollingHook', () => {
it('returns default props', () => {
const { result } = renderHook(() => usePollingHook());
expect(result.current.data).toEqual(null);
expect(result.current.busy).toEqual(false);
expect(result.current.isPolling).toEqual(false);
expect(result.current.setPolling).toEqual(expect.any(Function));
});
it('ensures the state is updated when the hook has polled a request', async (done) => {
const { result, waitFor } = renderHook(() => usePollingHook());
expect(result.current.busy).toEqual(false);
act(() => {
result.current.setPolling(true);
});
await waitFor(() => expect(result.current.isPolling).toEqual(true));
act(() => {
jest.advanceTimersByTime(10000);
});
await waitFor(() => expect(result.current.busy).toEqual(true));
expect(requestAdaptor).toBeCalled();
await waitFor(() => expect(result.current.busy).toEqual(false));
expect(result.current.data).toEqual(1);
done();
});
});
requestAdaptor.js
一些伪造的 http 函数会环绕一个承诺,例如模拟 axios 或 fetch
export function requestAdaptor() {
return new Promise((resolve) => {
return setTimeout(() => {
return resolve({ userId: 1 });
}, 1000);
});
}
export default requestAdaptor;