ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Hardhat 3 使用 hardhat-toolbox-mocha-ethers 编写 ethers.js + Mocha 测试的完整指南

Hardhat 3 使用 hardhat-toolbox-mocha-ethers 编写 ethers.js + Mocha 测试的完整指南 Hardhat 3 使用 hardhat-toolbox-mocha-ethers 编写 ethers.js Mocha 测试的完整指南【免费下载链接】hardhatHardhat is a development environment to compile, deploy, test, and debug your Ethereum software.项目地址: https://gitcode.com/GitHub_Trending/ha/hardhat本指南以 SKILL.md 为核心骨架系统讲解在 Hardhat 3 项目中通过nomicfoundation/hardhat-toolbox-mocha-ethers工具包编写 TypeScript 测试的完整工作流如何用network.create()建立连接、用ethers对象完成部署与合约交互、借助 TypeChain 获得编译期类型检查以及使用hardhat-ethers-chai-matchers插件断言交易回滚、事件与 ETH 余额变化。读完本文你将能直接照搬这些代码模式为基于 ethers.js Mocha 的 Hardhat 3 项目写出可运行、类型安全、断言完备的测试套件。1. 本技能在整个测试栈中的定位hardhat-toolbox-mocha-ethers技能是核心hardhat技能SKILL.md之上的工具箱配套层。它面向的是以下技术栈组合连接库使用ethers.jsv6TypeScript 测试运行在Mocha全局describe/it之上断言使用chai并叠加hardhat-ethers-chai-matchers插件提供的以太坊专属 matcher。使用前应当先加载hardhat核心技能掌握测试组织方式、network.create()返回的对象结构、networkHelpers、fixture 机制和先编译、后类型检查的工作流。本技能描述的所有对象都挂在network.create()返回的连接上。1.1 工具箱聚合了哪些插件从工具箱的插件入口 packages/hardhat-toolbox-mocha-ethers/src/index.ts 的dependencies列表可以看到definePlugin一次性加载了 8 个依赖插件nomicfoundation/hardhat-ethers连接库与 signernomicfoundation/hardhat-ethers-chai-matchers以太坊断言nomicfoundation/hardhat-ignition-ethersIgnition 部署集成nomicfoundation/hardhat-keystore密钥库nomicfoundation/hardhat-mochaMocha 测试运行层nomicfoundation/hardhat-network-helpersnetworkHelpersnomicfoundation/hardhat-typechainTypeChain 类型生成nomicfoundation/hardhat-verify合约验证这些插件的类型通过 type-extensions.ts 统一对外导出因此只需安装一个包即可获得完整的能力。安装方式参见 README.mdnpm install --save-dev nomicfoundation/hardhat-toolbox-mocha-ethers然后在 Hardhat 配置中注册import { defineConfig } from hardhat/config; import hardhatToolboxMochaEthers from nomicfoundation/hardhat-toolbox-mocha-ethers; export default defineConfig({ plugins: [hardhatToolboxMochaEthers], });也可以用npx hardhat --init选择 A TypeScript Hardhat project using Mocha and Ethers.js 直接初始化示例项目。2. Mocha 测试文件的连接初始化Mocha 不会awaitdescribe回调因此连接必须放在文件顶部、用顶层await建立Hardhat 3 仅支持 ESM天然具备顶层await能力import { expect } from chai; import { network } from hardhat; const { ethers, networkHelpers } await network.create(); describe(Counter, function () { // ... });network.create()每次调用都会产生独立的区块链状态详见 SKILL.md 中对连接 API 的说明。本技能的约定是一个文件一个连接是常态测试之间的状态隔离不靠为每个describe新建连接而是靠loadFixture来自networkHelpers机制见 hardhat 核心技能。3. ethers 对象signer 与合约交互3.1 类型来源与编译顺序合约类型由TypeChain从编译后的 ABI 生成。因此在类型检查之前必须先运行构建命令让类型反映最新的合约npx hardhat build npx tsc --noEmit这一步是 Hardhat 3 的核心工作流类型检查器会在你运行测试之前就捕获错误的参数类型、缺失的参数以及非法的选项例如给非 payable 函数传value。3.2 常用 API 全景// Provider只读链上数据 const blockNumber await ethers.provider.getBlockNumber(); const balance await ethers.provider.getBalance(0xabc...); // Signers用于发送交易 const [owner, alice, bob] await ethers.getSigners(); // 默认的带余额账户 // 工具函数 const oneEth ethers.parseEther(1.0); // 部署合约返回完整类型化的实例。 // 第二个参数传构造参数第三个参数可选传非默认 signer。 const counter await ethers.deployContract(Counter); const counterWithArgs await ethers.deployContract(Counter, [42n]); const counterFromAlice await ethers.deployContract(Counter, [], alice); // 读取状态、调用函数。参数与返回值在编译期对照 ABI 做类型检查 // 传入错误的类型或错误的参数个数都是 TypeScript 编译错误。 const value await counter.x(); await counter.inc(); await counter.incBy(3n); // 用其他 signer 发送交易 await counter.connect(alice).inc(); // 随 payable 调用一起发送 ETH await counter.deposit({ value: oneEth }); // 挂载到已部署的合约 const existing await ethers.getContractAt(Counter, 0xabc...);3.3 在 fixture 中部署loadFixture的 setup 函数内ethers.deployContract是标准的部署步骤async function deployCounterFixture() { const counter await ethers.deployContract(Counter); return { counter }; } const { counter } await networkHelpers.loadFixture(deployCounterFixture);loadFixture会先执行一次 setup 并快照链上状态后续调用直接恢复快照而不是重新部署在大规模测试套件中能显著提速。注意 setup 必须是具名函数不能用箭头函数或匿名函数缓存才能生效。3.4 从任意地址发送交易getImpersonatedSigner如果需要从非默认注资账户default-funded accounts发送交易先为它注资以支付 gas再通过ethers.getImpersonatedSigner拉取一个模拟 signer。它内部自行处理了账户模拟无需再单独调用networkHelpers.impersonateAccountconst addr 0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa; await networkHelpers.setBalance(addr, ethers.parseEther(1.0)); const signer await ethers.getImpersonatedSigner(addr); await counter.connect(signer).inc();4. Chai matchers以太坊专属断言hardhat-ethers-chai-matchers插件通过 chai 的use()机制扩展了expect覆盖交易回滚、事件与余额变化三类断言。插件注册入口见 add-chai-matchers.ts它在内部还同时启用了chai-as-promiseduse(chaiAsPromised)这正是能把未 await 的交易 promise 直接传给expect的前提。使用要点把交易 promise即写调用的返回值、不 await传给expectimport { expect } from chai; // 回滚断言 await expect(counter.connect(banned).inc()).to.revert(ethers); await expect(counter.connect(banned).inc()).to.be.revertedWith( only the owner can increment the counter, ); await expect(counter.connect(banned).inc()).to.be.revertedWithCustomError( counter, Unauthorized, ); await expect(counter.connect(banned).inc()) .to.be.revertedWithCustomError(counter, Unauthorized) .withArgs(banned.address); // 事件断言 await expect(counter.inc()).to.emit(counter, Increment); await expect(counter.inc()).to.emit(counter, Increment).withArgs(1n); // ETH 余额变化正数 收到负数 支出在扣除 gas 之前比较 await expect(game.claim()).to.changeEtherBalance(ethers, winner, PRIZE); await expect(game.claim()).to.changeEtherBalances( ethers, [winner, loser], [PRIZE, -STAKE], );4.1 matcher 族的源码构成从 matchers 目录 可以看到 matcher 的完整实现族回滚类reverted/下的revert.ts、revertedWith.ts、revertedWithCustomError.ts、revertedWithPanic.ts、revertedWithoutReason.ts、legacyReverted.ts以及 panic 码解析panic.ts事件类emit.ts与withArgs.ts余额类changeEtherBalance.ts、changeEtherBalances.ts、changeTokenBalance.ts数值/格式类big-number.ts、hexEqual.ts、properAddress.ts、properHex.ts、properPrivateKey.ts、addressable.ts。以 changeEtherBalance.ts 为例其签名暴露了三个要点第一个参数是ethers对象本身HardhatEthers类型——这就是为什么调用时要写成.changeEtherBalance(ethers, winner, PRIZE)余额变化可以是精确的 BigInt如PRIZE也可以是一个谓词函数(change: bigint) boolean用于模糊匹配底层通过getBalances在交易前后分别取余额并计算差值以 wei 为单位支持options如是否计入 gas 费用并调用了preventAsyncMatcherChaining防止异步 matcher 链式误用。4.2 withArgs 与 anyValue.withArgs除了精确匹配参数值还可以配合anyValue谓词。由于anyValue需要显式导入工具箱文档建议单独安装nomicfoundation/hardhat-ethers-chai-matchersnpm install --save-dev nomicfoundation/hardhat-ethers-chai-matchersimport { anyValue } from nomicfoundation/hardhat-ethers-chai-matchers/withArgs; await expect(counter.inc()) .to.emit(counter, Increment) .withArgs(anyValue); // 不关心事件参数的具体值5. 普通断言的选择对于普通断言相等、数组、类型等直接使用 chai 自带的expect不需要也不应该动用以太坊专属 matcherimport { expect } from chai; expect(await counter.x()).to.equal(1n); expect(await counter.getOwner()).to.equal(owner.address);6. 一套完整的测试模板综合以上模式一个完整的 Counter 测试文件结构如下import { expect } from chai; import { network } from hardhat; const { ethers, networkHelpers } await network.create(); async function deployCounterFixture() { const counter await ethers.deployContract(Counter, [0n]); return { counter }; } describe(Counter, function () { it(starts at zero, async function () { const { counter } await networkHelpers.loadFixture(deployCounterFixture); expect(await counter.x()).to.equal(0n); }); it(emits Increment and changes balance when depositing, async function () { const { counter } await networkHelpers.loadFixture(deployCounterFixture); await expect(counter.deposit({ value: ethers.parseEther(1.0) })) .to.emit(counter, Deposit) .withArgs(ethers.parseEther(1.0)); await expect(counter.deposit({ value: ethers.parseEther(1.0) })).to.changeEtherBalance( ethers, await counter.getAddress(), ethers.parseEther(1.0), ); }); it(reverts for unauthorized callers, async function () { const { counter } await networkHelpers.loadFixture(deployCounterFixture); const [, banned] await ethers.getSigners(); await expect(counter.connect(banned).inc()).to.be.revertedWithCustomError( counter, Unauthorized, ); }); });运行测试npx hardhat test mocha # 运行 Mocha 层 TypeScript 测试完整命令语法与--coverage选项说明见 hardhat 核心技能。7. 小结hardhat-toolbox-mocha-ethers把 ethers.js 连接、TypeChain 类型、Mocha 运行层与 chai matchers 聚合为一个开箱即用的插件集插件入口。本技能给出的核心实践可以概括为四条文件顶部一次性建立连接Mocha 不 awaitdescribe回调用顶层await network.create()拿到{ ethers, networkHelpers }状态隔离靠loadFixture而不是反复新建连接先hardhat build再类型检查让 TypeChain 生成的合约类型与最新 ABI 同步写调用用 chai matcher 断言把未 await 的交易 promise 传给expect(...).to.emit / .to.be.revertedWith* / .to.changeEtherBalance(s)并善用.withArgs与anyValue。【免费下载链接】hardhatHardhat is a development environment to compile, deploy, test, and debug your Ethereum software.项目地址: https://gitcode.com/GitHub_Trending/ha/hardhat创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表