使用 renderHook 测试自定义钩子



我想测试一个自定义钩子,它是作为与其他钩子重用代码的帮助函数实现的。它在实现中调用useDispatchuseSelector,同时将数据保存在会话存储中:

export function useCustomHook(key, obj)
{
let myObject = {
somefield: obj.someField
};
sessionStorage.setItem(key, JSON.stringify(myObject));
const dispatch = useDispatch();
dispatch(actionCreator.addAction(key, myObject));
}

和测试:

it('should have data in redux and session storage', () =>
{
const obj = { 
somefield: 'my val', 
};
renderHook(() => useCustomHook('some key', obj));
let savedObj= JSON.parse(sessionStorage.getItem('some key'));
expect(savedObj.someField).toBe('my val');
renderHook(() => 
{
console.log("INSIDE");
let reduxObj = useSelector(state => state.vals);
console.log("THE OBJECT: " );
console.log(reduxObj);
expect(reduxObj).toBe(2); //just to see if it fails the test - it's not
});
})

无论我尝试什么,测试都只会到达"INSIDE"控制台日志,而不会打印"THE OBJECT:"控制台日志。测试本身仍然通过,所以就像useSelector以某种方式停止了 renderHook 执行的其余部分。

我猜这与测试没有连接商店的事实有关......在这种情况下,可以做些什么来测试 redux?

您需要提供一个wrapper组件,该组件添加 reduxProvider以及要连接到的store

it('should have data in redux and session storage', () =>
{
const obj = { 
somefield: 'my val', 
};
const store = {} // create/mock a store
const wrapper = ({ children }) => <Provider store={store}>{children}</Provider>
renderHook(() => useCustomHook('some key', obj), { wrapper });
let savedObj= JSON.parse(sessionStorage.getItem('some key'));
expect(savedObj.someField).toBe('my val');
renderHook(() => {
console.log("INSIDE");
let reduxObj = useSelector(state => state.vals);
console.log("THE OBJECT: " );
console.log(reduxObj);
expect(reduxObj).toBe(2); //just to see if it fails the test - it's not
}, { wrapper });
})

作为旁注,renderHook正在捕获错误,这就是为什么您在测试中看不到它们的原因,如果您尝试访问它会抛出result.current并且您可以看到它以result.error表示,但是这里的用法不从要断言的自定义钩子返回值是非常不寻常的。

这也可能会在第二个renderHook调用中出现断言而导致问题。 您可能希望从钩子返回值并在外部断言,或者在 redux 存储中断言更新的值。

您可以编写记录的 reduxrenderWithProviders函数的简单扩展。这看起来像:


import React, { PropsWithChildren } from 'react'
import { render } from '@testing-library/react'
import type { RenderOptions } from '@testing-library/react'
import { configureStore } from '@reduxjs/toolkit'
import type { PreloadedState } from '@reduxjs/toolkit'
import { Provider } from 'react-redux';
import { renderHook } from '@testing-library/react';

export function renderHookWithProviders<
Result,
Props>(
render: (initialProps: Props) => Result,
{
preloadedState = {},
// Automatically create a store instance if no store was passed in
store = configureStore({
reducer: {
....
},
preloadedState
}),
...renderOptions
}: ExtendedRenderOptions = {}
) {
function Wrapper({ children }: PropsWithChildren<{}>): JSX.Element {
return <Provider store={store}>{children}</Provider>
}
return { store, ...renderHook(render, { wrapper: Wrapper, ...renderOptions }) }
}

请注意,render方法已替换为@testing-library/react中的renderHook。然后可以简单地在测试中使用:

describe('useGetRelated', () => {
test('Return related', () => {
const { result } = renderHookWithProviders(() => useGetRelated(), {
preloadedState: {
//initial state of redux
}
});

});
});

钩子测试库也简要地讨论了这个问题。尽管每个测试中的解决方案需要更多的样板,但 redux 倡导的解决方案似乎更好 IMO。

相关内容

  • 没有找到相关文章

最新更新