vm.expectRevert
是 Foundry 中用于测试合约函数是否会抛出 revert
错误的一个非常有用的工具。它允许你验证在执行某个操作时,合约是否会按预期抛出特定的错误。这对编写安全和可靠的智能合约测试至关重要,尤其是在涉及访问控制、权限检查等场景时。
1. 基本功能
vm.expectRevert
用于设置期望捕获的错误。当你调用合约函数时,如果该函数触发了 revert
,expectRevert
会检查其是否与预期的错误类型一致。如果一致,测试通过;如果不一致,或者没有触发 revert
,测试失败。
2. 语法和使用
vm.expectRevert(bytes memory revertData);
revertData
:一个字节数组,表示你期望捕获的revert
数据。你可以传递错误选择器或者错误数据(错误选择器是 Solidity 错误的前四个字节,通常通过abi.encodeWithSelector
或abi.encodeWithSignature
编码)。
3. 常见用法
3.1. 捕获简单的 revert
错误
假设你有一个合约,其中的 onlyOwner
修饰符检查调用者是否为合约的拥有者:
function freezeAccount(address account) public onlyOwner {
// 只有拥有者可以调用
// 这里的错误是权限错误,非拥有者调用会触发 revert
revert("Ownable: caller is not the owner");
}
在测试中,你期望捕获这个 revert
错误:
function testOnlyOwnerCanFreeze() public {
address nonOwner = address(0x123);
// 使用 vm.expectRevert 捕获 `revert` 错误
vm.expectRevert("Ownable: caller is not the owner");
// 试图非拥有者调用,应该触发 revert
myToken.freezeAccount(nonOwner);
}
在这个例子中,vm.expectRevert("Ownable: caller is not the owner")
将捕获 revert
错误信息并验证它是否与预期的错误消息匹配。
3.2. 捕获自定义错误(Custom Errors)
自定义错误(在 Solidity 0.8+ 中引入)比传统的 revert
错误更加高效。它们通常用于减少 gas 消耗,并且不再依赖于字符串消息。
假设你在合约中定义了一个自定义错误:
// 自定义错误类型
error OwnableUnauthorizedAccount(address account);
function freezeAccount(address account) public onlyOwner {
// 如果调用者不是合约的拥有者,抛出自定义错误
revert OwnableUnauthorizedAccount(account);
}
在测试中,你可以通过 vm.expectRevert
捕获这个自定义错误:
function testOnlyOwnerCanFreeze() public {
address nonOwner = address(0x123);
// 使用 abi.encodeWithSelector 构造错误数据
vm.expectRevert(
abi.encodeWithSelector(OwnableUnauthorizedAccount.selector, nonOwner)
);
// 非拥有者调用时应该触发 revert
myToken.freezeAccount(nonOwner);
}
3.3. 捕获其他类型的 revert
错误
你可以通过 abi.encodeWithSelector
或 abi.encodeWithSignature
编码捕获任何 Solidity 错误。通常,错误可以分为两类:
- 标准错误:如
require
失败、revert
抛出带消息的错误。 - 自定义错误:通过
error
关键字定义的错误类型。
例如,如果你想捕获一个带有参数的错误:
// 自定义错误
error InsufficientBalance(address account, uint256 amount);
你可以使用以下代码来捕获这个错误:
vm.expectRevert(
abi.encodeWithSelector(InsufficientBalance.selector, address(0x123), 100)
);
3.4. 捕获没有任何错误消息的 revert
如果你期望捕获没有任何附加错误信息的 revert
,可以直接调用 vm.expectRevert()
,并且不传递任何参数:
function testShouldRevert() public {
vm.expectRevert(); // 仅检查是否 revert,但不关心具体错误消息
// 任何会导致 revert 的操作
myToken.transfer(address(0x123), 100);
}
4. 总结 vm.expectRevert
的关键点
vm.expectRevert()
是一个 Foundry 提供的测试工具,用于预期合约中的revert
错误。- 它可以捕获 普通的错误消息,或者 自定义错误。
- 它通过接受
bytes memory revertData
来检查revert
错误的数据。- 如果捕获的是 普通的
revert
错误,你可以直接传递错误消息字符串。 - 如果捕获的是 自定义错误,你需要使用
abi.encodeWithSelector
或abi.encodeWithSignature
来构建错误的数据。
- 如果捕获的是 普通的
5. 如何构建 revert
错误数据
- 普通
revert
错误:如果revert
只带有消息字符串,可以直接传递字符串。vm.expectRevert("Custom error message");
- 自定义错误:如果是 Solidity 0.8.x+ 引入的自定义错误,则需要使用
abi.encodeWithSelector
或abi.encodeWithSignature
来编码错误数据。vm.expectRevert(abi.encodeWithSelector(CustomError.selector, param1, param2));
6. 常见错误
- 没有发生
revert
错误时的错误:如果你期望某个函数会触发revert
错误,但实际上没有触发,则会导致测试失败。 - 错误数据不匹配时的错误:如果你期望捕获某个特定的错误,但错误数据不匹配(例如,错误的错误类型或错误参数),测试会失败。
声明:本作品采用署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0)进行许可,使用时请注明出处。
Author: mengbin
blog: mengbin
Github: mengbin92
cnblogs: 恋水无意
腾讯云开发者社区:孟斯特
—