You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

能否将Mocha Hardhat测试脚本中的合约部署逻辑复用至其他测试脚本?

方案合理性判断

这个方案完全合理,是Hardhat技术栈下优化测试效率的标准方案。它既解决了多测试文件重复部署合约的冗余耗时问题(合约量越大的项目优化效果越明显,通常可降低70%以上的测试执行耗时),又通过状态重置机制保证了不同测试文件、不同测试用例之间的状态隔离,不会出现跨用例的状态污染,完全符合测试的独立性要求。

行业实践情况

该方案已经是Hardhat官方推荐的测试优化手段,大量头部Web3项目(包括Uniswap、AAVE等成熟DeFi协议)的测试套件都采用了类似实现,生态成熟度很高,相关问题的踩坑和解决方案都很完善。

具体实现思路

核心原理是利用EVM快照+回滚机制替代重复的合约部署逻辑,你可以按照以下步骤实现:

  • 第一步:抽离公共部署逻辑为全局fixture
    把所有测试文件通用的合约部署逻辑封装为独立的异步函数,统一存放在公共目录(比如test/fixtures/),函数返回所有测试需要用到的合约实例、签名账号、配置参数等对象。示例代码如下:
    // test/fixtures/core.js
    const { ethers } = require("hardhat");
    
    async function deployCoreFixtures() {
      const [owner, ordinaryUser, adminUser] = await ethers.getSigners();
      // 部署所有通用核心合约
      const ERC20 = await ethers.getContractFactory("MyERC20");
      const erc20 = await ERC20.deploy(ethers.utils.parseEther("1000000"));
      await erc20.deployed();
      const MinePool = await ethers.getContractFactory("MinePool");
      const minePool = await MinePool.deploy(erc20.address);
      await minePool.deployed();
      // 抛出所有测试所需对象
      return { owner, ordinaryUser, adminUser, erc20, minePool };
    }
    
    module.exports = { deployCoreFixtures };
    
  • 第二步:在测试文件中复用fixture
    直接使用Waffle提供的loadFixture方法加载你封装的全局fixture即可。loadFixture底层已经实现了快照缓存逻辑:第一次调用时会执行完整部署并拍摄EVM快照,后续所有调用(不管是同一个测试文件的不同用例,还是不同测试文件的调用)都会直接回滚到快照状态,不会重复执行部署逻辑。示例代码如下:
    // test/minePool.test.js
    const { expect } = require("chai");
    const { loadFixture } = require("@nomiclabs/hardhat-waffle");
    const { deployCoreFixtures } = require("./fixtures/core");
    
    describe("矿池合约测试", function () {
      it("测试正常质押逻辑", async function () {
        // 加载全局fixture,后续用例调用会直接回滚快照
        const { minePool, ordinaryUser, erc20 } = await loadFixture(deployCoreFixtures);
        // 后续测试逻辑
        await erc20.connect(ordinaryUser).approve(minePool.address, ethers.utils.parseEther("100"));
        await minePool.connect(ordinaryUser).stake(ethers.utils.parseEther("100"));
        expect(await minePool.userStakeBalance(ordinaryUser.address)).to.equal(ethers.utils.parseEther("100"));
      });
    
      it("测试正常赎回逻辑", async function () {
        const { minePool, ordinaryUser, erc20 } = await loadFixture(deployCoreFixtures);
        // 该用例拿到的合约状态和上一个用例完全独立,不会受到上一个用例操作的影响
        await erc20.connect(ordinaryUser).approve(minePool.address, ethers.utils.parseEther("100"));
        await minePool.connect(ordinaryUser).stake(ethers.utils.parseEther("100"));
        await minePool.connect(ordinaryUser).withdraw(ethers.utils.parseEther("100"));
        expect(await minePool.userStakeBalance(ordinaryUser.address)).to.equal(0);
      });
    })
    
  • 可选优化:如果你的测试集很大,还可以基于Mocha的全局钩子实现全局快照,所有测试文件共用同一个快照,进一步降低重复开销。如果部分测试文件需要额外部署定制合约,也可以在全局fixture的基础上扩展二级fixture,同样可以用loadFixture做缓存。

内容的提问来源于stack exchange,提问作者Juan Ignacio Pérez Sacristán

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.04 12:30:01