如何使用Jest正确模拟Web3默认导出的构造函数以返回指定模拟对象?
Let's break down why your current setup isn't working first, then fix it:
The Root Cause
When you use new with a constructor function in JavaScript, if the constructor returns a primitive value (like your string 'Hello, world!'), JavaScript ignores that return value and instead returns the newly created instance object. That's why you're seeing mockConstructor {} instead of your expected string—your mock's return value is being discarded automatically.
Fix 1: Mock the Constructor to Return a Custom Object (Recommended)
Since Web3 instances are objects by design, you should mock the constructor to return an object with the properties/methods your tests need. Here's how to adjust your code:
import Web3 from 'web3'; jest.mock('web3', () => { // Return a mock constructor that creates our custom instance return jest.fn().mockImplementation(() => ({ // Add any Web3 instance properties/methods your test relies on here eth: { requestAccounts: jest.fn().mockResolvedValue(['0x1234567890abcdef']) }, // Optional: Add a toString method to mimic your original expected output toString: () => 'Hello, world!' })); }); describe('app', () => { test('mock web3', () => { console.log('web3:', Web3); const web3 = new Web3(window.ethereum); console.log("new web3:", web3); // Logs the custom mock object console.log("new web3 as string:", web3.toString()); // Logs 'Hello, world!' // Verify the constructor was called with the correct argument expect(Web3).toHaveBeenCalledWith(window.ethereum); }); });
Fix 2: Simulate a "Primitive-like" Instance (Edge Case)
If you absolutely need to mimic a primitive return (though this isn't realistic for actual Web3 usage), you can create an object that behaves like a string:
jest.mock('web3', () => { return jest.fn().mockImplementation(() => { const mockInstance = Object.create(String.prototype); Object.assign(mockInstance, 'Hello, world!'); return mockInstance; }); });
This creates an object that acts like a string (e.g., web3 === 'Hello, world!' will return true) but remains technically an object, which aligns with JavaScript's constructor rules.
Why Your Previous Attempts Didn't Work
- Moving the
importafterjest.mock()has no effect because Jest automatically hoists alljest.mock()calls to the top of the module. - Switching to
require()doesn't change JavaScript's core behavior around constructor return values—primitive returns are still ignored.
内容的提问来源于stack exchange,提问作者lmat - Reinstate Monica

