【发布时间】:2020-10-15 12:09:43
【问题描述】:
我有很多调用 dispatch() (Redux) 的功能性 React 组件。 dispatch() 函数本身是由 react-redux 的 useDispatch() 钩子传入的。
作为一个简化的例子:
const LogoutButton: FC = () => {
const dispatch: Dispatch = useDispatch();
return (
<button onClick={() => {
console.log("Clicked...") // To check that onClick() simulation is working
dispatch(authActions.logout())
}}>
LOGOUT
</button>
)
}
使用 Jest 和 Enzyme,我需要做什么才能断言 expect(dispatch).toHaveBeenCalledWith(authActions.logout)?
我没有模拟商店或使用 redux-mock-store 的功能。相反,我将组件包装在我为测试而制作的 Root 组件中。它与我真正的 Root 组件相同,但需要道具来设置测试的初始存储(和 history.location):
const TestRoot: FC<RootProps> = ({ children, initialState = {}, initialEntries = defaultLocation }) => {
const store = createStore(reducers, initialState, applyMiddleware(thunk));
return (
<Provider store={store}>
<MemoryRouter initialEntries={initialEntries}>
<ScrollToTop />
<StylesProvider jss={jss}>
<ThemeProvider theme={theme}>{children}</ThemeProvider>
</StylesProvider>
</MemoryRouter>
</Provider>
);
};
在测试中用于设置 Enzyme-wrapped 组件,如:
wrappedLogoutButton = mount(
<TestRoot initialState={initialState}>
<LogoutButton />
</TestRoot>
);
这个设置对我来说很有效(到目前为止),如果我不需要,我不想更改它。我确实尝试将一个 redux-mock-store 模拟存储注入到 TestRoot 中,但这搞砸了我编写的每一个测试套件。
我尝试了多种方法来模拟或监视dispatch() 和useDispatch(),但我无法看到模拟被调用。 onClick() 模拟正在运行,因为我可以看到正在记录“点击...”。这是一个示例测试(真实代码,不是简化示例):
test('should log learner out', () => {
const event = {
preventDefault: () => {},
target: { textContent: en.common['log-out-title'] } as unknown,
} as React.MouseEvent<HTMLButtonElement, MouseEvent>;
const wrappedLogOut = wrappedMenuDrawer.find(ListItem).at(3).find('a');
// @ts-ignore
act(() => wrappedLogOut.prop('onClick')(event));
// TODO: test authService.logout is called
// assert that dispatch was called with authActions.logout()
expect(amplitude.logAmpEvent).toHaveBeenCalledWith('from main menu', { to: 'LOGOUT' }); // This assertion passes
});
根据文档、Medium 帖子和 Stack Overflow 上的类似问题,我尝试过在调度时模拟/监视的方法/变体,包括:
import * as reactRedux from 'react-redux'
const spy = jest.spyOn(reactRedux, 'useDispatch'
import store from '../../store';
const spy = jest.spyOn(store, 'dispatch')
const mockUseDispatch = jest.fn();
const mockDispatch = jest.fn();
jest.mock('react-redux', () => ({
useDispatch: () => mockUseDispatch.mockReturnValue(mockDispatch),
}));
// or...
jest.mock('react-redux', () => ({
useDispatch: () => mockUseDispatch,
}));
mockUseDispatch.mockReturnValue(mockDispatch) // inside the actual test
【问题讨论】:
-
我需要做什么才能断言 - 请参阅enzymejs.github.io/enzyme/docs/api/ReactWrapper/simulate.html。 onClick() 模拟正在工作,因为我可以看到“Clicked...”被记录 - 它不是,并且在您发布的代码中没有单击模拟。 console.log 和 dispatch 是在渲染时调用的,而不是在点击时调用的,顺便说一句,onClick 有语法错误,它不会编译。应该是
onClick={() => dispatch(authActions.logout())},而不是onClick={dispatch(authActions.logout())}。 -
感谢@EstusFlask。我发布的测试代码不是完整的测试,只是我在调度时模拟/监视的尝试。我一直在使用
act(() => wrappedButton.prop('onClick')());的变体来模拟点击。您发现的语法错误(很好的发现)是 StackOverflow 帖子上的错字,但不是我的实际代码。我将修正错字并从我的代码中添加一个实际测试。如果您仍然认为模拟点击有问题,请告诉我,但我认为没有。 -
一个地方是
a,另一个地方是button。 我一直在使用变体 - 究竟是什么变体?直接调用 onClick 并不是真正的点击模拟(但可能还可以)。此外,您不需要act和 Enzyme。您可以为您的问题提供stackoverflow.com/help/mcve 吗?该问题仅包含截断且不一致的 sn-ps,无法说明您做错了什么。如果有断言失败的错误消息,请发布。
标签: reactjs redux react-redux jestjs enzyme