如何Mock Web3.js中以太坊智能合约调用?遇TypeError问题求助
解决Web3.js合约调用模拟的TypeError问题
你遇到的TypeError: Cannot read properties of undefined (reading 'balanceOf'),本质是mock的层级不完整——Web3的合约调用是链式结构,从web3Client.eth.Contract到contract.methods.balanceOf再到.call(),每一层都需要正确mock,不能出现undefined的中间层。
下面是用mockDeep(结合Jest/ts-jest)的完整解决方案,精准模拟你需要的两段核心逻辑:
1. 完整Mock结构示例
import { mockDeep, mockReset } from 'jest-mock-extended'; import { Web3 } from 'web3'; import { getERC20Balance } from './your-file-path'; // 替换为你的目标函数路径 // 全局mock Web3类 jest.mock('web3', () => ({ Web3: jest.fn().mockImplementation(() => mockDeep<Web3>({ eth: { // 模拟Contract构造函数,返回带有methods的合约实例 Contract: jest.fn().mockReturnValue(mockDeep({ methods: { // 模拟balanceOf方法,返回带有call方法的对象 balanceOf: jest.fn().mockReturnValue({ call: jest.fn().mockResolvedValue('1000000000000000000') // 模拟1 ETH对应的wei值 }) } })) }, utils: { fromWei: jest.fn().mockReturnValue('1') // 模拟wei转ether的结果 } })) })); describe('getERC20Balance', () => { let mockWeb3: Web3; beforeEach(() => { mockWeb3 = new Web3(); mockReset(mockWeb3); // 重置mock状态,避免用例间污染 }); it('should return correct token balance', async () => { const result = await getERC20Balance(mockWeb3, '0xTokenAddress', '0xWalletAddress'); // 验证Contract构造函数被正确调用 expect(mockWeb3.eth.Contract).toHaveBeenCalled(); // 验证balanceOf方法被传入钱包地址调用 expect(mockWeb3.eth.Contract().methods.balanceOf).toHaveBeenCalledWith('0xWalletAddress'); // 验证最终结果符合预期 expect(result).toBe('1'); }); });
2. 关键Mock点说明
- 不能跳过任何链式层级:必须确保
web3Client.eth、web3Client.eth.Contract、contract.methods、contract.methods.balanceOf、balanceOf().call()每一层都被mock为非undefined的对象/函数,否则会触发属性读取错误。 - 异步方法的mock:
.call()是异步方法,要用mockResolvedValue模拟Promise返回结果;同步方法如fromWei用mockReturnValue即可。 - 构造函数的mock:
web3Client.eth.Contract是构造函数,需要手动指定它的返回值结构,mockDeep无法自动感知构造函数的返回类型。
3. 错误排查补充
如果仍出现类似错误,检查:
- 是否正确mock了
getERC20ABIOfInterest函数(如果它是外部依赖),确保返回合法的balanceOf ABI片段。 - 确认测试中传入的
web3Client是mock后的实例,而非真实Web3实例。 - 打印
contract、contract.methods、contract.methods.balanceOf的值,定位哪一层是undefined。
内容的提问来源于stack exchange,提问作者Umer
相关产品推荐
相关产品推荐

