solidity
是以太坊上首选的智能合约开发语言,有很多人都用它进行智能合约的学习与开发;而truffle
是以太坊上最流行的开发框架,深受大家的喜爱。
在开发过程中,不可避免的需要对solidity
代码进行测试。因为能够按需定制、灵活、方便的原因,大部分人选择的都是基于JS
来书写测试代码,因此以下的小技巧都是基于JS
测试中遇到的一些困惑之处,总结一下与大家共勉。
编码风格的选择: .THEN
与ASYNC/AWAIT
因为异步执行的关系,truffle
中的JS
测试有两种编码风格:
-
.then
风格
it("should put 10000 MetaCoin in the first account", function() {
return MetaCoin.deployed().then(function(instance) {
return instance.getBalance.call(accounts[0]);
}).then(function(balance) {
assert.equal(balance.valueOf(), 10000, "10000 wasn't in the first account");
});
});
-
ASYNC/AWAIT
风格
it("should put 10000 MetaCoin in the first account", async () => {
let instance = await MetaCoin.deployed();
let balance = await instance.getBalance.call(accounts[0]);
assert.equal(balance.valueOf(), 10000);
})
这两种风格其实执行的测试内容都一样,但明显ASYNC/AWAIT
风格的更适合书写以及阅读——更有美感;而且,更重要的是,修改测试代码时更不容易出错(亲测使用.then
风格修改时非常痛苦),所以强烈推荐使用ASYNC/AWAIT
风格来书写测试代码。
如何测试事件
事件(event
)是solidity
中用来与前端进行交互的机制,使用的范围很广。但是作为新手时,对于如何测试事件真的是感觉无从下手。
参考库合约openzeppelin
中关于测试事件的写法:
function awaitEvent (event, handler) {
return new Promise((resolve, reject) => {
function wrappedHandler (...args) {
Promise.resolve(handler(...args)).then(resolve).catch(reject);
}
event.watch(wrappedHandler);
});
}
it("should emit GotPayed", async () => {
let instance = await PersonalPayment.new();
let event = instance.GotPayed({});
let watcher = async function (err, result) {
event.stopWatching();
if (err) { throw err; }
assert.equal(result.args._amount, 10);
};
await instance.payPayments([accounts[1]], [10], {value: web3.toWei('11', 'ether')});
await awaitEvent(event, watcher);
})
其基本思路还是web3
接口中的方法:
- 设置好
event
- 写好处理的
watcher
函数,在awaitEvent
函数中通过Promise
来进行异步处理
不过,在实际的测试中,发现了一份更加精简的方案:
it("should emit GotPayed", async () => {
let instance = await PersonalPayment.new();
await instance.payPayments([accounts[1]], [10], {value: web3.toWei('11', 'ether')});
let event = await new Promise((resolve, reject) => {
instance.GotPayed((err, log) => {
if(!err) {resolve(log);}
})
});
assert.equal(event.args._amount, 10);
})
思路上是模拟的与前端进行交互的方式,在这里加了一层基于Promise
的异步调用即可。可以看出,在实际中逻辑更简单,代码也更加简洁
如何模拟时间跨度进行测试
在测试中,有时候需要模拟一段时间跨度来测试代码的逻辑,但是在没有教程的情况下,这一步完全是没有思路。我们先上一个例子,再来解读一下:
await web3.currentProvider.send({
jsonrpc: "2.0",
method: "evm_increaseTime",
params: [10],
id: 0
});
await web3.currentProvider.send({
jsonrpc: "2.0",
method: "evm_mine",
params: [],
id: 0
});
- 通过
web3.currentProvider.send
方法执行了两条命令:web3.currentProvider
是web3
接口中的方法,获取当前的网络provider
;而接下来的send()
则是发起一次RPC
调用,输入的是JSON RPC
格式的参数来指明要执行的方法:-
method: "evm_increaseTime
:表示执行的方法 -
params: [10]
:表示要执行方法的参数
-
-
evm_increaseTime
与evm_mine
皆是来自于Ganache CLI
的特有方法:Ganache CLI
是开发框架truffle
中内置的个人化区块链组件,主要用于模拟以太坊来方便开发、测试与部署;也是当前我们采用的环境。-
evm_increaseTime
:增加区块时间,是我们模拟时间跨度主要需要的功能 -
evm_mine
:强制进行一次区块打包,主要是为了让前面执行evm_increaseTime
的动作生效
-
在完成这些操作之后,就可以进行正常的测试逻辑了。
网友评论