本文档是 OpenZeppelin Test Helpers 的 API 参考,介绍了用于测试以太坊智能合约的各种辅助函数。主要包括:用于检查账户余额的 balance,用于处理大数的 BN,常用的常量 constants,以及用于模拟时间和快照的 time 和 snapshot 等辅助函数,expectEvent 和 expectRevert 用于测试事件和异常。
本文档正在编写中:如有疑问,请前往 tests 目录 查看有关如何使用每个 helper 的示例。
所有返回的数字都是 BN 类型。
balance用于检查特定帐户的以太币余额的 Helper。
所有这些函数都返回 BN 实例,默认情况下余额以 'wei' 为单位。
currentasync function balance.current(account, unit = 'wei')
返回帐户的当前余额。
const balance = await balance.current(account)
// 与 new BN(web3.eth.getBalance(account)) 相同
const balanceEth = await balance.current(account, 'ether')
// 与 new BN(web3.utils.fromWei(await web3.eth.getBalance(account), 'ether')) 相同
trackerasync function balance.tracker(account, unit = 'wei')
创建余额追踪器的实例,使你可以跟踪帐户以太币余额的变化。
const tracker = await balance.tracker(account)
tracker.getasync function tracker.get(unit = tracker.unit)
返回帐户的当前余额。
const tracker = await balance.tracker(account) // 实例化
const currentBalance = await tracker.get() // 返回帐户的当前余额
tracker.deltaasync function tracker.delta(unit = tracker.unit)
返回自上次查询以来(使用 get、 delta 或 deltaWithFees)余额的变化。
const tracker = await balance.tracker(receiver, 'ether')
send.ether(sender, receiver, ether('10'))
(await tracker.delta()).should.be.bignumber.equal('10');
(await tracker.delta()).should.be.bignumber.equal('0');
或使用 get():
const tracker = await balance.tracker(account) // 实例化
const currentBalance = await tracker.get() // 返回帐户的当前余额
(await tracker.delta()).should.be.bignumber.equal('0');
追踪器还可以返回特定单位的所有余额和增量:
const tracker = await balance.tracker(account, 'gwei');
const balanceGwei = tracker.get(); // 以 gigawei 为单位
const balanceEther = tracker.get('ether'); // 以 ether 为单位
tracker.deltaWithFeesasync function tracker.deltaWithFees(unit = tracker.unit)
返回一个对象,该对象包含自上次查询以来(使用 get、 delta 或 deltaWithFees)余额的变化,以及在此期间支付的 gas 费。
const tracker = await balance.tracker(account, 'gwei');
...
const { delta, fees } = await tracker.deltaWithFees();
BN一个 bn.js 对象。 使用 new BN(number) 创建 BN 实例。
constants有用的常量的集合。
ZERO_ADDRESSaddress 类型变量的初始值,即 Solidity 中的 address(0)。
ZERO_BYTES32bytes32 类型变量的初始值,即 Solidity 中的 bytes32(0x00)。
MAX_UINT256以 BN 表示的最大无符号整数 2\^256 - 1。
MAX_INT256以 BN 表示的最大有符号整数 2\^255 - 1。
MIN_INT256以 BN 表示的最小有符号整数 -2\^255。
ether将以太币中的值转换为 wei。
expectEventfunction expectEvent(receipt, eventName, eventArgs = {})
断言 receipt 中的日志包含一个名称为 eventName 的事件,并且参数与 eventArgs 中指定的参数匹配。 receipt 应该是由 web3 Contract 或 truffle-contract 调用返回的对象。
const web3Receipt = await MyWeb3Contract.methods.foo('bar').send();
expectEvent(web3Receipt, 'Foo', { value: 'bar' });
const truffleReceipt = await MyTruffleContract.foo('bar');
expectEvent(truffleReceipt, 'Foo', { value: 'bar' });
请注意,可以保留一些或所有未指定的事件参数:expectEvent 将仅检查提供的参数,而忽略其余参数。
const receipt = await MyWeb3Contract.methods.foo('bar').send();
// 这两个断言都将通过
expectEvent(receipt, 'Foo', { value: 'bar' });
expectEvent(receipt, 'Foo');
inTransactionasync function expectEvent.inTransaction(txHash, emitter, eventName, eventArgs = {})
与 expectEvent 相同,但适用于由任意合约(emitter,合约实例)在任意交易(哈希 txHash)中发出的事件,即使它是被间接调用的(即如果它是由另一个智能合约而非外部拥有的帐户调用的)。
注意:emitter 必须是发出预期事件的已部署合约实例。
// 使用 web3 合约
const contract = await MyContract.deploy().send();
const { transactionHash } = await contract.methods.foo('bar').send();
await expectEvent.inTransaction(transactionHash, contract, 'Foo', { value: 'bar' });
// 使用 truffle 合约
const contract = await MyContract.new();
const { txHash } = await contract.foo('bar');
await expectEvent.inTransaction(txHash, contract, 'Foo', { value: 'bar' });
inConstructionasync function expectEvent.inConstruction(emitter, eventName, eventArgs = {})
与 inTransaction 相同,但适用于在 emitter 的构造期间发出的事件。 请注意,目前仅 truffle 合约支持此功能。
notEmitted为了测试是否未发出事件,可以使用 expectEvent.notEmitted。 有几个变体遵循先前提到函数的 API:
expectEvent.notEmitted(receipt, eventName) 类似于 expectEvent()
expectEvent.notEmitted.inTransaction(txHash, emitter, eventName) 类似于 expectEvent.inTransaction()
expectEvent.notEmitted.inConstruction(emitter, eventName) 类似于 expectEvent.inConstruction()
expectRevertasync function expectRevert(promise, message)
用于交易失败的 Helper(类似于 chai 的 throw):断言由于交易回滚而拒绝了 promise。
它还将检查回滚原因是否包含 message。 当回滚原因未知时,请使用 expectRevert.unspecified。
例如,给定以下合约:
contract Owned {
address private _owner;
constructor () {
_owner = msg.sender;
}
function doOwnerOperation() public view {
require(msg.sender == _owner, "Unauthorized");
....
}
}
可以按如下方式测试 doOwnerOperation 函数中的 require 语句:
const { expectRevert } = require('@openzeppelin/test-helpers');
const Owned = artifacts.require('Owned');
contract('Owned', ([owner, other]) => {
beforeEach(async function () {
this.owned = Owned.new({ from: owner });
});
describe('doOwnerOperation', function() {
it('Fails when called by a non-owner account', async function () {
await expectRevert(
this.owned.doOwnerOperation({ from: other }),
"Unauthorized"
);
});
});
...
unspecifiedasync function expectRevert.unspecified(promise)
与 expectRevert 类似,断言由于 require 或 revert 语句导致交易回滚而拒绝了 promise,但不检查回滚原因。
assertionasync function expectRevert.assertion(promise)
断言由于 assert 语句或无效操作码导致交易回滚而拒绝了 promise。
outOfGasasync function expectRevert.outOfGas(promise)
断言由于交易耗尽 gas 而拒绝了 promise。
makeInterfaceIdERC165function makeInterfaceId.ERC165(interfaces = [])
给定一系列函数签名,计算合约的 ERC165 接口 ID。
ERC1820function makeInterfaceId.ERC1820(name)
给定合约的名称,计算合约的 ERC1820 接口哈希。
sendetherasync function send.ether(from, to, value)
将 value 以太币从 from 发送到 to。
transactionasync function send.transaction(target, name, argsTypes, argsValues, opts = {})
将交易发送到合约 target,使用 argValues 调用方法 name,argValues 的类型为 argTypes(根据方法的签名)。
singletonsERC1820Registryasync function singletons.ERC1820Registry(funder)
返回按照规范部署的 ERC1820Registry 的实例(即,注册表位于规范地址)。 可以多次调用此函数以检索同一实例。
timeadvanceBlockasync function time.advanceBlock()
强制挖掘一个区块,从而增加区块高度。
advanceBlockToasync function time.advanceBlockTo(target)
强制挖掘区块,直到达到目标区块高度。
注意:使用此函数推进太多区块会大大降低你的测试速度。 尽量减少其使用。
latestasync function time.latest()
返回最新挖掘区块的时间戳。 应与 advanceBlock 结合使用以检索当前的区块链时间。
latestBlockasync function time.latestBlock()
返回最新挖掘的区块号。
increaseasync function time.increase(duration)
将区块链的时间增加 duration (以秒为单位),并使用该时间戳挖掘一个新区块。
increaseToasync function time.increaseTo(target)
与 increase 相同,但指定了目标时间而不是持续时间。
durationfunction time.duration()
用于将不同的时间单位转换为秒的 Helper。 可用的 Helper 有:seconds、minutes、hours、days、weeks 和 years。
await time.increase(time.duration.years(2));
snapshotasync function snapshot()
返回一个快照对象,其中包含“restore”函数,该函数将区块链恢复到捕获的状态。
const snapshotA = await snapshot()
// ...
await snapshotA.restore()
- 原文链接: docs.openzeppelin.com/te...
- 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!