mcpField
H
hardhat-toolbox-mocha-ethers

hardhat-toolbox-mocha-ethers

github.com/nomicfoundation测试与质量248 次安装2 周前更新
代码仓库

关于 hardhat-toolbox-mocha-ethers

为核心 `hardhat` 技能补充使用 ethers、Mocha 和 chai 编写智能合约测试的指南,面向 Hardhat 项目开发者。

主要功能

  1. 01配置 Mocha 测试与网络连接
  2. 02使用 ethers 签名者和合约交互
  3. 03通过 fixture 部署合约
  4. 04模拟地址并为账户注资
  5. 05断言交易、事件与余额变化

适用场景

  1. 01测试合约读取和状态变更调用
  2. 02在测试间复用部署设置
  3. 03检查回滚行为和触发的事件

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/nomicfoundation/hardhat-skills --skill hardhat-toolbox-mocha-ethers

安装后在 Claude Code 中输入/hardhat-toolbox-mocha-ethers确认已加载。

详细介绍

Hardhat 工具箱:ethers + Mocha

此技能基于核心 hardhat 技能。请先加载该技能,了解测试组织方式、network.create() 的结构、networkHelpers、fixture 和类型检查工作流。以下内容都基于 network.create() 返回的连接。

此技术栈使用 Mocha(全局 describe / it)运行测试,并使用 chai 进行断言。Mocha 不会等待 describe 回调,因此要在文件顶部使用顶层 await 来建立连接:

import { expect } from "chai";
import { network } from "hardhat";

const { ethers, networkHelpers } = await network.create();

describe("Counter", function () {
  // ...
});

通常每个文件建立一个连接。测试之间的状态隔离通过 loadFixture(参见 hardhat 技能)实现,而不是在每个 describe 中创建新的连接。

ethers:签名者和合约交互

合约类型由 TypeChain 根据编译后的 ABI 生成。请在进行类型检查前运行 hardhat build,确保类型反映最新的合约。

// Provider (read-only chain data)
const blockNumber = await ethers.provider.getBlockNumber();
const balance = await ethers.provider.getBalance("0xabc...");

// Signers (for sending transactions)
const [owner, alice, bob] = await ethers.getSigners(); // default funded accounts

// Utilities
const oneEth = ethers.parseEther("1.0");

// Deploy a contract, returns a fully typed instance.
// Pass constructor args as the second argument, and (optionally) a signer
// other than the default as the third.
const counter = await ethers.deployContract("Counter");
const counterWithArgs = await ethers.deployContract("Counter", [42n]);
const counterFromAlice = await ethers.deployContract("Counter", [], alice);

// Read state and call functions. Args and return types are type-checked
// against the ABI at compile time; passing wrong types or wrong argument
// counts is a TypeScript error.
const value = await counter.x();
await counter.inc();
await counter.incBy(3n);

// Send from a different signer
await counter.connect(alice).inc();

// Send ETH along with a payable call
await counter.deposit({ value: oneEth });

// Attach to an already-deployed contract
const existing = await ethers.getContractAt("Counter", "0xabc...");

在 loadFixture 设置函数内部(周边模式请参见 hardhat 技能),ethers.deployContract 是标准的部署步骤:

async function deployCounterFixture() {
  const counter = await ethers.deployContract("Counter");
  return { counter };
}

const { counter } = await networkHelpers.loadFixture(deployCounterFixture);

若要从任意地址(而非默认获得资金的账户)发送交易,请为其提供 gas 费,并使用 ethers.getImpersonatedSigner 获取一个模拟签名者——它会在内部处理模拟,无需单独调用 networkHelpers.impersonateAccount:

const addr = "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
await networkHelpers.setBalance(addr, ethers.parseEther("1.0"));
const signer = await ethers.getImpersonatedSigner(addr);
await counter.connect(signer).inc();

Chai 匹配器:针对以太坊的断言

hardhat-ethers-chai-matchers 插件为 chai 的 expect 扩展了用于交易、事件和余额变化的匹配器。将交易 Promise(写入调用的结果,不要使用 await)传入 expect:

import { expect } from "chai";

// Reverts
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);

// Events
await expect(counter.inc()).to.emit(counter, "Increment");
await expect(counter.inc()).to.emit(counter, "Increment").withArgs(1n);

// ETH balance changes (positive = received, negative = spent, before gas)
await expect(game.claim()).to.changeEtherBalance(ethers, winner, PRIZE);
await expect(game.claim()).to.changeEtherBalances(
  ethers,
  [winner, loser],
  [PRIZE, -STAKE],
);

对于普通断言(相等性、数组、类型),直接使用 chai 的 expect,无需以太坊匹配器。

评论

支持 Markdown