Truffle中JS测试小技巧

solidity是以太坊上首选的智能合约开发语言,有很多人都用它进行智能合约的学习与开发;而truffle是以太坊上最流行的开发框架,深受大家的喜爱。

在开发过程中,不可避免的需要对solidity代码进行测试。因为能够按需定制、灵活、方便的原因,大部分人选择的都是基于JS来书写测试代码,因此以下的小技巧都是基于JS测试中遇到的一些困惑之处,总结一下与大家共勉。

编码风格的选择: .THENASYNC/AWAIT

因为异步执行的关系,truffle中的JS测试有两种编码风格:

  1. .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");
  });
});
  1. 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接口中的方法:

  1. 设置好event
  2. 写好处理的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
});
  1. 通过web3.currentProvider.send方法执行了两条命令:web3.currentProviderweb3接口中的方法,获取当前的网络provider;而接下来的send()则是发起一次RPC调用,输入的是JSON RPC格式的参数来指明要执行的方法:
    • method: "evm_increaseTime:表示执行的方法
    • params: [10]:表示要执行方法的参数
  2. evm_increaseTimeevm_mine皆是来自于Ganache CLI的特有方法:Ganache CLI是开发框架truffle中内置的个人化区块链组件,主要用于模拟以太坊来方便开发、测试与部署;也是当前我们采用的环境。
    • evm_increaseTime:增加区块时间,是我们模拟时间跨度主要需要的功能
    • evm_mine:强制进行一次区块打包,主要是为了让前面执行evm_increaseTime的动作生效

在完成这些操作之后,就可以进行正常的测试逻辑了。

你可能感兴趣的:(Truffle中JS测试小技巧)