如果您有一个React组件调用一个获取数据的自定义钩子,那么在测试React组件时模拟内部自定义钩子结果的最佳方法是什么?我看到2种主要方法:
1)嘲笑自定义钩子。这似乎是最推荐的方法,但是似乎它要求测试对内部实现细节以及可能需要模拟的内容比组件的props接口建议的内容有更多的了解(假设使用prop-type或TypeScript)
2)使用依赖项注入方法。将钩子声明为道具,但将其默认设置为真实的钩子,这样就不必在渲染组件的任何地方都设置钩子,而是允许使用模拟覆盖进行测试。这是一个人为的codeandbox示例,其中包含一个模拟自定义钩子的测试:
2需要更多的键入内容,但似乎更易于测试。但是,测试已经必须具有组件的内部实现细节的知识,才能测试呈现输出的任何条件逻辑,所以也许这并不重要,而1是最佳方法。是要走的路吗?您看到什么权衡?我是否完全错过了另一种方法?
答案 0 :(得分:3)
使用玩笑来嘲笑您的自定义钩子。
import * as useCustomHook from '../hooks/useCustomHooks'
const spy = jest.spyOn(useCustomHook, 'default')
spy.mockReturnValue({
name: 'test'
})
答案 1 :(得分:1)
这个问题还有几个月的历史了,但是如果您找不到一个好的解决方案,我写了一个可能会有所帮助的软件包。我经历了类似的思考过程,包括“如果将钩子注入组件该怎么办?”事情变得很奇怪。
我基本上想要一个连接器,以避免额外的包装用于演示组件以进行测试。
我想出了react-hooks-compose
,它使您可以将钩子和演示者分开,并分别或一起进行测试:https://www.npmjs.com/package/react-hooks-compose
export const useFetch = () => {
const [user, setUser] = useState();
useEffect(() => {
fetchData('some-url') // <-- Fetches data on mount
.then(res => setUser(res.data));
}, []);
return {user};
}
// composeHooks passes the values from your hooks as props
export const UserPresenter = ({user}) => {
return <div>You fetched data for: {user.name}</div>;
}
export default composeHooks({ useFetch })(DataPresenter);
现在您不必模拟这个钩子,您只需使用一个道具来测试演示者:
it('presents user', () => {
const { queryByText } = render(<UserPresenter user={{name: 'Mary'}} />); // <-- Named export
expect(queryByText('Mary')).toBeTruthy();
});
或者,您可以选择更高级别的集成测试:
it('fetches data', () => {
fetchData.mockResolvedValue('Mary');
const { queryByText } = render(<UserWithData />); // <-- Default export
expect(queryByText('Mary')).toBeFalsy();
return wait(() => {
expect(queryByText('Mary')).toBeTruthy();
});
});
如果愿意,甚至可以对钩子进行单元测试。
答案 2 :(得分:0)
有了模拟钩子本身,如果真正的钩子与您的组件完全兼容,您现在再也不会。
以传递钩子为道具,很难使钩子彼此通信。例如。当您需要自定义钩子以从同一组件useState
调用setter时。您将需要使用越来越多的参数来扩展自定义钩子。
fetch
或XHR
。它仍然需要知道一些实现细节-您正在运行HTTP请求-但是您的测试应该了解的东西更少。