如何使用Enzyme测试TypeScript React组件的Context回调函数?
Hey there! Let's break down how to properly test that Context callback function in your TypeScript React component—this is a super common gotcha, so I’ve got you covered.
First, let’s align on a typical setup (since you didn’t share your exact code, I’ll use a representative example that matches your scenario).
Example Context & Component
Say your Context is defined like this:
// MyContext.tsx import { createContext, useContext } from 'react'; // Define the Context type for TypeScript safety type AppContextType = { callContextCallback: (payload: string) => void; }; // Create context with a fallback (we'll enforce it's never undefined via our hook) export const AppContext = createContext<AppContextType | undefined>(undefined); // Custom hook to avoid repeating null checks export const useAppContext = () => { const context = useContext(AppContext); if (!context) throw new Error('useAppContext must be used within an AppContext.Provider'); return context; };
And your component uses this Context like so:
// MyComponent.tsx import { useAppContext } from './MyContext'; export const MyComponent = () => { const { callContextCallback } = useAppContext(); const handleUserAction = () => { // This is the callback we want to test callContextCallback('user-triggered payload'); }; return <button onClick={handleUserAction}>Run Context Callback</button>; };
The Correct Testing Approach (Using React Testing Library + Jest/Vitest)
The key mistake people often make is either not providing the Context Provider in their test, or mocking the callback incorrectly. Here’s the step-by-step fix:
Create a mock callback function
Use Jest’sjest.fn()(or Vitest’svi.fn()) to create a trackable mock of your callback.Wrap your component in the Context Provider
When rendering the component in your test, pass the mock callback as part of the Context’s value. This ensures your component has access to the mock instead of the real implementation.Trigger the user interaction
Simulate the action that should fire the callback (like clicking the button in our example).Assert the mock was called
Verify the mock was invoked the correct number of times, with the expected arguments.
Here’s the full test code:
// MyComponent.test.tsx import { render, screen, fireEvent } from '@testing-library/react'; import { MyComponent } from './MyComponent'; import { AppContext } from './MyContext'; describe('MyComponent', () => { it('triggers the Context callback when the button is clicked', () => { // 1. Create the mock callback const mockCallContextCallback = jest.fn(); // 2. Render component wrapped in Context Provider with mock value render( <AppContext.Provider value={{ callContextCallback: mockCallContextCallback }}> <MyComponent /> </AppContext.Provider> ); // 3. Simulate the user action const triggerButton = screen.getByRole('button', { name: /run context callback/i }); fireEvent.click(triggerButton); // 4. Assert the mock was called correctly expect(mockCallContextCallback).toHaveBeenCalledTimes(1); expect(mockCallContextCallback).toHaveBeenCalledWith('user-triggered payload'); }); });
Common Pitfalls to Avoid
- Forgot the Context Provider: If you render the component without wrapping it in the Provider, your custom hook will throw an error (since we enforced non-null Context) or the callback will be undefined—this is the #1 reason for "function not called" failures.
- Mock Scope Issues: Always create the mock callback inside each test case (or in a
beforeEach) to avoid cross-test contamination. If you reuse a mock across tests, previous calls will linger and cause false failures. - TypeScript Type Errors: Make sure the value you pass to the Provider matches the
AppContextTypeexactly. Don’t passundefined—your custom hook already guards against that, so your test can safely pass a full, valid value. - Asynchronous Callbacks: If your callback is async (e.g., it makes an API call), use
waitForto wrap your assertion:await waitFor(() => { expect(mockCallContextCallback).toHaveBeenCalled(); });
For Vitest Users
The logic is identical—just replace jest.fn() with vi.fn():
const mockCallContextCallback = vi.fn();
That’s it! This approach ensures you’re properly isolating the component and verifying that it interacts with the Context as expected.
内容的提问来源于stack exchange,提问作者besrabasant

