Jest未覆盖useEffect内部函数,如何测试组件异步请求函数?
fetchSomeData covered by test coverage? Let's break down why your test isn't covering the fetchSomeData function, and how to fix it step by step.
Root Causes
1. Incorrect Function Declaration Order
In your Component1, you're calling fetchSomeData inside useEffect before declaring the function with const. Since const declarations don't get hoisted, when the useEffect callback runs on component mount, fetchSomeData doesn't exist yet. This means the function never actually executes—so coverage tools can't track it as "used".
2. Unhandled Asynchronous Effect Execution
Your test uses shallow rendering, but you're not wrapping the render logic in act() to handle React's asynchronous side effects. Even if the function was declared correctly, without act(), the useEffect callback might not execute in a way that the test (and coverage tool) can capture.
Fixes
Step 1: Fix the Component's Function Order
First, reorder your code so fetchSomeData is declared before it's used in useEffect:
import React, { useEffect } from 'react'; function Component1() { // Declare the function first so it exists when useEffect runs const fetchSomeData = async () => { console.log('fetchSomeData'); }; useEffect(() => { fetchSomeData(); }, []); return <div>Component1</div>; } export default Component1;
Step 2: Update the Test to Track and Verify the Function Call
To ensure the function executes and gets counted in coverage, you need to either track its execution directly or verify its side effects. Here are two common approaches:
Approach 1: Extract and Mock the Function
If you want direct control over mocking and verifying the function, extract it from the component and export it:
// Component1.js export const fetchSomeData = async () => { console.log('fetchSomeData'); }; function Component1() { useEffect(() => { fetchSomeData(); }, []); return <div>Component1</div>; } export default Component1;
Then update your test to mock the function and verify it's called:
import React from 'react'; import { act } from 'react-dom/test-utils'; import Enzyme, { shallow } from 'enzyme'; import Adapter from 'enzyme-adapter-react-16'; import Component1, { fetchSomeData } from './Component1.js'; Enzyme.configure({ adapter: new Adapter() }); jest.mock('./Component1.js', () => { const originalModule = jest.requireActual('./Component1.js'); return { ...originalModule, fetchSomeData: jest.fn(), }; }); describe('Component1', () => { it('calls fetchSomeData on component mount', async () => { await act(async () => { shallow(<Component1 />); }); expect(fetchSomeData).toHaveBeenCalled(); }); });
Approach 2: Verify Side Effects (Without Modifying Component Structure)
If you don't want to extract the function, you can mock a side effect (like console.log) to confirm the function ran:
import React from 'react'; import { act } from 'react-dom/test-utils'; import Enzyme, { shallow } from 'enzyme'; import Adapter from 'enzyme-adapter-react-16'; import Component1 from './Component1.js'; Enzyme.configure({ adapter: new Adapter() }); describe('Component1', () => { it('executes fetchSomeData on mount', async () => { // Mock console.log to track calls const consoleSpy = jest.spyOn(console, 'log').mockImplementation(); await act(async () => { shallow(<Component1 />); }); expect(consoleSpy).toHaveBeenCalledWith('fetchSomeData'); // Restore console.log to its original state consoleSpy.mockRestore(); }); });
Key Takeaways
- Always declare functions before using them in hooks like
useEffect(sinceconst/letdon't hoist). - Use
act()to wrap any code that triggers React state updates or side effects—this ensures all asynchronous operations complete before assertions run. - For coverage tracking, make sure the function actually executes during tests (fixing the order and using
act()will resolve this).
内容的提问来源于stack exchange,提问作者jjzjx118_2

