React Testing Library测试中onClick触发Context内dispatch函数报错的解决方案咨询
The error you're hitting is super clear: your component expects to pull passwordModalDispatch and formDisplayDispatch from their respective Contexts, but your test only passes the state objects (passwordModalState and formDisplayState) in the Provider values. When the button clicks trigger those dispatch calls, the component can't find the functions—hence the TypeError.
Here's how to fix this, plus best practices for testing Context-dependent components:
Step 1: Mock the Dispatch Functions
Use Jest's jest.fn() to create mock dispatch functions. These will let the component call them without errors, and later you can assert that they were called with the exact arguments you expect.
Step 2: Update Your Test Context Values
Include the mock dispatch functions in the objects you pass to each Context Provider's value prop, matching the shape the component expects (since it uses object destructuring from useContext).
Revised Test Code
test('triggers correct dispatch actions when registration button is clicked', () => { // 1. Create mock dispatch functions const mockPasswordModalDispatch = jest.fn(); const mockFormDisplayDispatch = jest.fn(); // 2. Build complete Context values with both state AND dispatch const passwordModalContext = { passwordModalState: { isOpen: false }, passwordModalDispatch: mockPasswordModalDispatch // Add this missing function }; const formContext = { formDisplayState: { form: 'login' }, formDisplayDispatch: mockFormDisplayDispatch // Add this missing function }; const { container } = render( <FormDisplayContext.Provider value={formContext}> <PasswordModalContext.Provider value={passwordModalContext}> <HelpCards /> </PasswordModalContext.Provider> </FormDisplayContext.Provider> ); // 3. Trigger the button click const registerButton = screen.getByText(/register for online access/i); userEvent.click(registerButton); // 4. Assert the dispatch functions were called correctly expect(mockPasswordModalDispatch).toHaveBeenCalledWith({ type: 'CLOSE' }); expect(mockFormDisplayDispatch).toHaveBeenCalledWith({ type: 'REGISTRATION' }); });
Bonus: Test the Login Button Flow
You can easily extend this to test the "Log in" card by adjusting the formDisplayState and updating your assertions:
test('triggers correct dispatch actions when login button is clicked', () => { const mockPasswordModalDispatch = jest.fn(); const mockFormDisplayDispatch = jest.fn(); const passwordModalContext = { passwordModalState: { isOpen: false }, passwordModalDispatch: mockPasswordModalDispatch }; // Set form to a non-login value so the "Already registered?" card renders const formContext = { formDisplayState: { form: 'registration' }, formDisplayDispatch: mockFormDisplayDispatch }; render( <FormDisplayContext.Provider value={formContext}> <PasswordModalContext.Provider value={passwordModalContext}> <HelpCards /> </PasswordModalContext.Provider> </FormDisplayContext.Provider> ); const loginButton = screen.getByTestId('loginBtn'); userEvent.click(loginButton); expect(mockPasswordModalDispatch).toHaveBeenCalledWith({ type: 'CLOSE' }); expect(mockFormDisplayDispatch).toHaveBeenCalledWith({ type: 'LOGIN' }); });
Key Takeaway
When testing components that consume Context, always make sure the Provider's value matches the exact shape the component expects. If your Context uses both state and dispatch (like most Redux-style Context setups), you need to include both in your test mocks. Using jest.fn() lets you verify that the component is dispatching the right actions, which is a critical part of testing stateful components.
内容的提问来源于stack exchange,提问作者snazzyy

