测试使用 useEffect 挂钩和 apollo 的自定义上下文挂钩
Testing a custom context hook that uses a useEffect hook and apollo
我创建了一个公开挂钩的上下文以便于使用。
在这个钩子中,我已经确保在呈现页面之前预加载了一些数据,如下所示:
export const MyContext = React.createContext({} as any);
function useMyContext() {
const context = React.useContext(MyContext);
if (context === undefined) {
throw new Error('useMyContext must be used within a MyContext');
}
return context;
}
function MyContextProvider(props: any) {
const client = useApolloClient();
const { user } = React.useContext(UserContext);
const [data, setData ] = React.useState({});
const findSomethingFromUser = () => {
return client.query({
query: FIND_SOMETHING_FROM_USER,
variables: { userId: user.id },
});
};
const load = () => {
findSomethingFromUser()
.then(({ data, errors }) => {
setData(data);
});
};
// Load user test
React.useEffect(load, []);
return (
<MyContext.Provider value={{ data }}>
{children}
</MyContext.Provider>
);
}
export { MyContextProvider, useMyContext };
我想使用 testing-library 进行测试,在阅读了一些文章和 github 问题之后,我得出以下结论:
const 包装器 = ({ children }) => (
{children}
);
it('should fetch the special user value', async () => {
const { result, waitForNextUpdate } = renderHook(useMyContext, { wrapper });
await waitForNextUpdate();
// await act(async () => {
// await waitForNextUpdate();
// });
expect(result.current.mySpecialUserValue).toEqual("something");
});
遗憾的是,当前为空。我预计这是因为 useEffect
导致状态更新,因此 return 首先是空值(默认值)。更新前。这就是我介绍 waitForNextUpdate
.
的原因
然而,它给了我以下错误:
警告:传递给 TestRenderer.act(...) 函数的回调不能 return 任何东西。
It looks like you wrote TestRenderer.act(async () => ...) or returned a Promise from it's callback. Putting asynchronous logic inside TestRenderer.act(...) is not supported.
console.error node_modules/react-test-renderer/cjs/react-test-renderer.development.js:102
Warning: Do not await the result of calling TestRenderer.act(...), it is not a Promise.
console.error node_modules/react-test-renderer/cjs/react-test-renderer.development.js:102
Warning: An update to MyContextProvider inside a test was not wrapped in act(...).
When testing, code that causes React state updates should be wrapped into act(...):
act(() => {
/* fire events that update state */
});
/* assert on the output */
关于如何解决这个问题有什么想法吗?
阅读 https://dev.to/theactualgivens/testing-react-hook-state-changes-2oga 后,我决定模拟 useReducer
,因为分派导致 useEffect
挂钩中的状态更新。之后它起作用了。我现在模拟 reducer 的初始状态。
const dispatch = jest.fn();
const useReducerSpy = jest.spyOn(React, 'useReducer');
useReducerSpy.mockImplementation((init: any) => [MY_CONTEXT_MOCK, dispatch]);
我的测试看起来像
it('should render', async () => {
const { result } = renderHook(useMyContext, { wrapper: bySlugWrapper });
expect(result.current.somevar).toEqual(MY_CONTEXT.somevar);
});
我创建了一个公开挂钩的上下文以便于使用。 在这个钩子中,我已经确保在呈现页面之前预加载了一些数据,如下所示:
export const MyContext = React.createContext({} as any);
function useMyContext() {
const context = React.useContext(MyContext);
if (context === undefined) {
throw new Error('useMyContext must be used within a MyContext');
}
return context;
}
function MyContextProvider(props: any) {
const client = useApolloClient();
const { user } = React.useContext(UserContext);
const [data, setData ] = React.useState({});
const findSomethingFromUser = () => {
return client.query({
query: FIND_SOMETHING_FROM_USER,
variables: { userId: user.id },
});
};
const load = () => {
findSomethingFromUser()
.then(({ data, errors }) => {
setData(data);
});
};
// Load user test
React.useEffect(load, []);
return (
<MyContext.Provider value={{ data }}>
{children}
</MyContext.Provider>
);
}
export { MyContextProvider, useMyContext };
我想使用 testing-library 进行测试,在阅读了一些文章和 github 问题之后,我得出以下结论:
const 包装器 = ({ children }) => ( {children} );
it('should fetch the special user value', async () => {
const { result, waitForNextUpdate } = renderHook(useMyContext, { wrapper });
await waitForNextUpdate();
// await act(async () => {
// await waitForNextUpdate();
// });
expect(result.current.mySpecialUserValue).toEqual("something");
});
遗憾的是,当前为空。我预计这是因为 useEffect
导致状态更新,因此 return 首先是空值(默认值)。更新前。这就是我介绍 waitForNextUpdate
.
然而,它给了我以下错误:
警告:传递给 TestRenderer.act(...) 函数的回调不能 return 任何东西。
It looks like you wrote TestRenderer.act(async () => ...) or returned a Promise from it's callback. Putting asynchronous logic inside TestRenderer.act(...) is not supported.
console.error node_modules/react-test-renderer/cjs/react-test-renderer.development.js:102
Warning: Do not await the result of calling TestRenderer.act(...), it is not a Promise.
console.error node_modules/react-test-renderer/cjs/react-test-renderer.development.js:102
Warning: An update to MyContextProvider inside a test was not wrapped in act(...).
When testing, code that causes React state updates should be wrapped into act(...):
act(() => {
/* fire events that update state */
});
/* assert on the output */
关于如何解决这个问题有什么想法吗?
阅读 https://dev.to/theactualgivens/testing-react-hook-state-changes-2oga 后,我决定模拟 useReducer
,因为分派导致 useEffect
挂钩中的状态更新。之后它起作用了。我现在模拟 reducer 的初始状态。
const dispatch = jest.fn();
const useReducerSpy = jest.spyOn(React, 'useReducer');
useReducerSpy.mockImplementation((init: any) => [MY_CONTEXT_MOCK, dispatch]);
我的测试看起来像
it('should render', async () => {
const { result } = renderHook(useMyContext, { wrapper: bySlugWrapper });
expect(result.current.somevar).toEqual(MY_CONTEXT.somevar);
});