使用Jest Mock portable-fetch遇阻:import*方式失效,require正常
require to ES Modules (import) for portable-fetch I’ve run into this exact scenario before—CommonJS mocks work smoothly, but switching to ES modules can break your mocks because Jest handles the two module systems differently. Let’s break down why this happens and how to fix it.
Why the require Version Worked
When using require, Jest’s jest.setMock can intercept the CommonJS module loading process before your code imports the module. ES modules, however, are statically analyzed at compile time, so jest.setMock doesn’t get a chance to replace the module before your code references it.
Solution 1: Use jest.mock to Mock portable-fetch Directly
This is the most straightforward fix for ES modules. Jest automatically hoists jest.mock calls to the top of your file, ensuring the mock is in place before your imports run.
Here’s how to adjust your test code:
// Import the mock fetch first import fetch from 'jest-fetch-mock'; // Import your factory module import factory from './current-weather'; // Mock the portable-fetch module to export our jest-fetch-mock instance jest.mock('portable-fetch', () => fetch); describe('current-weather', () => { const config = { appid: 'appid' }; const currentWeather = factory(config); // Reset the mock before each test to avoid cross-test pollution beforeEach(() => { fetch.resetMocks(); }); test('currentWeather()', async () => { // Set up your mock response fetch.mockResolvedValue({ json: async () => ({ // Add your mock API response data here main: { temp: 20 }, name: 'Test City' }) }); // Run the function under test const result = await currentWeather(); // Assert the mock was called correctly expect(fetch).toHaveBeenCalledTimes(1); expect(fetch).toHaveBeenCalledWith( // Match the expected API URL your function uses expect.stringContaining(`appid=${config.appid}`) ); // Add more assertions for the result as needed expect(result.temp).toBe(20); }); });
Solution 2: Ensure Jest is Configured for ES Modules
If you’re still having issues, double-check your Jest configuration to make sure it handles ES modules properly:
Update
jest.config.js:module.exports = { transform: { // Use babel-jest to transpile ES modules '^.+\\.jsx?$': 'babel-jest', }, moduleFileExtensions: ['js', 'jsx'], // If using TypeScript, add 'ts' and 'tsx' to moduleFileExtensions };Check Babel Configuration:
Make sure your Babel setup includes@babel/preset-envto transpile ES modules to CommonJS for Jest. Your.babelrcorbabel.config.jsonshould look something like:{ "presets": [ ["@babel/preset-env", { "targets": { "node": "current" } }] ] }
Solution 3: Use jest.spyOn for Granular Control
If you need to mock only specific exports or restore the original module after tests, use jest.spyOn:
import * as portableFetch from 'portable-fetch'; import factory from './current-weather'; import fetch from 'jest-fetch-mock'; describe('current-weather', () => { const config = { appid: 'appid' }; const currentWeather = factory(config); let fetchSpy; beforeEach(() => { // Spy on the default export of portable-fetch and replace it with our mock fetchSpy = jest.spyOn(portableFetch, 'default').mockImplementation(fetch); fetch.resetMocks(); }); afterEach(() => { // Restore the original fetch implementation after each test fetchSpy.mockRestore(); }); test('currentWeather()', async () => { fetch.mockResolvedValue({ json: async () => ({ main: { temp: 25 } }) }); const result = await currentWeather(); expect(fetchSpy).toHaveBeenCalled(); expect(result.temp).toBe(25); }); });
The key takeaway is that ES modules require Jest to handle mocking at the static analysis stage, so jest.mock (or jest.spyOn) is the right tool instead of jest.setMock.
内容的提问来源于stack exchange,提问作者user2771738

