用途:这是 openzeppelin-contracts 仓库的测试规范,供编写或修改测试、mock 及形式化验证规格时参考,指导在不同场景选择何种测试手段。
方法选择:默认使用 Hardhat+Chai 单元测试作为基线,覆盖正常路径与边界情况;当某性质可用带符号输入的 Foundry 测试表达时,用 Halmos 符号执行(快速、无需外部凭证);数学密集、数据结构或复杂不变量适合 Foundry 模糊测试;当合约含状态机或访问控制规则复杂到模糊测试无法覆盖时,升级到 Certora 形式化验证。
内部函数访问:hardhat-exposed 为 contracts/ 中每个合约自动生成 $ContractName 包装合约,将内部 _function 重新暴露为外部 $_function,产物输出到 contracts-exposed/(已 gitignore)。可直接使用,例如部署 '$ERC20' 后调用 token.$_mint(holder, 1000n);不要手写 $ 前缀包装器。
手工 mock:放在 contracts/mocks/(非 test/),镜像 contracts/ 目录结构。仅当 hardhat-exposed 无法满足时才写,如构造期设置、多扩展组合、注入覆盖 hook 的行为、对抗性行为或无对应实现的公开包装。即使手工 mock 也优先复用自动生成的 $_ 函数。
Hardhat+Chai:用 loadFixture 并提供命名函数作为 fixture,避免内联匿名函数导致每次重跑;结构为 describe('ContractName') > describe('methodName') > it();共享行为用 .behavior.js 中的 shouldBehaveLike* 函数;多目标测试可在顶层循环。断言使用 bigint 字面量、.revertedWithCustomError、.revertedWithPanic、.emit 与 .changeTokenBalance 等。
Foundry:.t.sol 与 Hardhat 测试同放 test/,HH3 下由 npm test 一并执行。以 test 开头且带类型参数的函数用于模糊测试;以 symbolic 或 testSymbolic 开头的用于 Halmos 符号执行(CI 匹配 --match-test '^symbolic|^testSymbolic'),同时也会作为普通模糊测试运行。模糊设置位于 hardhat.config.ts 的 test.solidity.fuzz。
Certora:规格位于 fv/specs/,为 .conf 加 .spec 对,CI 在带 formal-verification 标签的 PR 上运行,需 CERTORAKEY。本地流程:make -C fv apply 应用 harness 补丁,node fv/run.js AccessControl 运行指定规格,node fv/run.js --all 运行全部。
变更集:任何改变合约行为的 PR 都需添加 changeset,执行 npx changeset add,文件写入 .changeset/,用 patch 表缺陷修复、minor 表新功能,描述一句话且以反引号包裹的组件名开头。仅改 NatSpec、无用户可见影响的内部重构或纯仓库杂项可跳过。
CI 矩阵:lint、tests(JS 加 Foundry,含继承与 pragma 检查)、tests-upgradeable(转译后重跑并比对存储布局)、coverage、slither、halmos 均在每次推送或 PR 触发;Certora 形式化验证在相应标签 PR 触发。
限制:资料未说明具体收费与价格、平台差异或性能数据;bug 修复必须附带可复现该缺陷的最小测试,目标为每个 PR 达到 100% 分支覆盖。