跳到主要内容

本地测试合约

本地测试提供快速迭代与确定性可复现性。但本地 EVM 模拟器无法完全建模 OXN 的机密性行为。本页解释本地能测什么、不能测什么、如何组织你的测试金字塔。

本地网络能测的

标准 Solidity 合约逻辑——所有在以太坊上无修改能跑的东西——都能在本地正常测试:

  • 合约状态转移
  • 访问控制(用 msg.sender
  • 事件 emit(topic 与 data 结构)
  • Revert 与错误条件
  • 多合约交互
  • Gas 使用模式(近似)

本地网络不能测的

以下行为依赖 OXN 运行时,不只是 EVM:

  • 加密 calldata 处理——本地网络看到 calldata 都是明文,任何关于"这次调用是否加密到达?"的断言在本地没有意义。
  • 机密存储——本地存储公开可读;"外部无法读这个槽"的测试在任何本地网络上都会假绿。
  • 签名查询——签名查询路径需要 OXN 网关。本地 eth_call 默认无认证。
  • 机密预编译——Sapphire.randomBytesSapphire.encrypt 等在标准 Hardhat network 上不存在。
  • PUSH0 拒绝——本地网络(Hardhat、Anvil)默认实现 PUSH0。用 shanghai 编译的合约在本地跑得好好的,只有到真 OXN 才失败。

推荐的测试金字塔

层级位置覆盖范围
单元测试Hardhat / Foundry 本地业务逻辑、数学、访问控制、revert 条件。
集成测试OXN 测试网机密性行为、加密 calldata、签名查询。
端到端测试OXN 测试网 + 真实钱包钱包交互、水龙头流程、面向用户的路径。

每个 dApp 都应该在真实 OXN 上跑至少一部分集成测试。 不要假设 Hardhat 绿了就代表生产绿。

Hardhat 本地测试

标准 Hardhat 测试开箱即用:

test/MyToken.test.js
const { expect } = require("chai");
const { ethers } = require("hardhat");

describe("MyToken", function () {
it("mints initial supply to deployer", async function () {
const [deployer] = await ethers.getSigners();
const Token = await ethers.getContractFactory("MyToken");
const token = await Token.deploy(ethers.parseUnits("1000", 18));

expect(await token.balanceOf(deployer.address))
.to.equal(ethers.parseUnits("1000", 18));
});
});

npx hardhat test 运行。Hardhat 起一个内存网络、部署、断言。

Foundry 本地测试

test/MyToken.t.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

import "forge-std/Test.sol";
import "../src/MyToken.sol";

contract MyTokenTest is Test {
MyToken token;
address deployer = address(this);

function setUp() public {
token = new MyToken(1_000_000 ether);
}

function testInitialSupply() public {
assertEq(token.balanceOf(deployer), 1_000_000 ether);
}
}

forge test 运行。Foundry 内建的 Test 合约提供断言辅助方法与 cheatcode。

Fixture 与快照

用 fixture 避免重复部署,测试更快:

Hardhat 的 loadFixture
const { loadFixture } = require("@nomicfoundation/hardhat-network-helpers");

async function deployTokenFixture() {
const [deployer, other] = await ethers.getSigners();
const Token = await ethers.getContractFactory("MyToken");
const token = await Token.deploy(ethers.parseUnits("1000", 18));
return { token, deployer, other };
}

describe("MyToken transfers", function () {
it("transfers tokens", async function () {
const { token, deployer, other } = await loadFixture(deployTokenFixture);
await token.transfer(other.address, ethers.parseUnits("100", 18));
expect(await token.balanceOf(other.address))
.to.equal(ethers.parseUnits("100", 18));
});
});

Fixture 在 setUp 后快照网络状态、每次测试前恢复,全套测试快 10-100 倍。

时间与区块操纵

Hardhat 与 Foundry 都暴露 cheatcode 用于在本地测试里把时间推进或设置特定区块条件。对时间锁定合约很有用:

Hardhat time cheatcodes
const { time } = require("@nomicfoundation/hardhat-network-helpers");

// 前进 1 小时
await time.increase(3600);

// 跳到指定时间戳
await time.increaseTo(1700000000);
Foundry time cheatcodes
vm.warp(block.timestamp + 3600); // 前进 1 小时
vm.roll(block.number + 100); // 推进 100 个区块

这些 cheatcode 只在本地。真实 OXN 不接受客户端的时间操纵。

下一步