Testing Your Smart Contracts With JavaScript (Including Async/Await Promises)

[ The code for this blog post can be found here: https://github.com/gustavoguimaraes/smart-contract-testing-javascript-example-]

Smart contract developers strive to mitigate bugs in their code as they can be literally costly. For this end, one of the techniques we use is to write tests extensively or dare I say obsessively to the codebase.

In this post I will show you how to start testing your smart contracts from the get go.

Show me the green light to move forward

First off, these are the dependencies I used to create this tutorial. If you come across an error, you can try install ingand using these versions.

- node@8.5.0
- truffle@^4.0.0
- ethereumjs-testrpc@^4.0.1

With the dependencies out of the way, let’s set up the project.

mkdir smart-contract-test-example && cd "$_" && truffle init webpack

The snippet above creates a directory, changes into it and initializes Trufflewith webpack.

Now create the test file for the FundRaise smart contract we’ll build.

touch test/fundRaise.js

Open the file in your favorite text editor and add the most basic test structure.

const FundRaise = artifacts.require('./FundRaise.sol')
contract('FundRaise', function () {
})

They first line fetches the contract artifacts. It is the contract abstraction containing its primary information i.e. its ABI, address and the like.

Then we create the function contract() which is similar to the describe()function in Mocha, except that Truffle adds some features such as making sure the contracts are deployed before running the tests. By the way, Truffle uses the Mocha testing framework as well as Chai assertion library under the hood.

Now let’s run this barebones test.

First:

testrpc

Then open a new command line window and type

truffle test test/fundRaise.js

The outcome is:

Error: Could not find artifacts for ./FundRaise.sol from any sources
    at Resolver.require (/usr/local/lib/node_modules/truffle/build/cli.bundled.js:49072:9)
    at TestResolver.require (/usr/local/lib/node_modules/truffle/build/cli.bundled.js:89733:30)
...

This is because we haven’t coded up the FundRaise smart contract just yet. Let’s fix this.

touch contracts/FundRaise.sol

Then add the code

pragma solidity ^0.4.17;
contract FundRaise {
}

Run the test again:

 0 passing (1ms)

Right. No tests written yet.

Smart Contract Specification and Adding Tests

The FundRaise smart contract will be a simple contract. Its straightforward specification is:

  • it has an owner
  • it accepts funds
  • it is able to pause and un-pause the fundraising activity
  • contract owner is able to remove funds from the contract to herself at any time.

Let’s start with the first specification — the contract has a owner

const FundRaise = artifacts.require('./FundRaise.sol')
contract('FundRaise', function ([owner]) {
    let fundRaise
    beforeEach('setup contract for each test', async function () {
        fundRaise = await FundRaise.new(owner)
    })
    it('has an owner', async function () {
        assert.equal(await fundRaise.owner(), owner)
    })
})

In the test code above we accomplish a few things.

1- fancy ES2015 destructuring variable assignment in function([owner] the first parameter given to the contract function is an array with the accounts coming from testrpc. We are taking the first one and assigning it the variable owner .

2- create the fundRaise variable

3- have a beforeEach function which will run before each test creating a new instance of fundRaise each time. Note the use of async/await for promises. This allows for more readable test code. If you want to read up more on the new JavaScript async/await features, this is a good blog post on it.

4- create the first test within the it() function block. Here we are asserting that the fundRaise.owner() is the owner that we passed to when creating the contract.

Before running the tests once more, head over to truffle.js and require babel-polyfill as we need it to use async/await.

truffle.js

// Allows us to use ES6 in our migrations and tests.
require('babel-register')
require('babel-polyfill')
module.exports = {
  networks: {
    development: {
      host: 'localhost',
      port: 8545,
      network_id: '*' // Match any network id
    }
  }
}

Run the tests again and you will find this error:

...
1 failing
1) Contract: FundRaise has an owner:
     AssertionError: expected undefined to equal '0x676c48fb3979cf2e47300e8ce80a99087589650d'
...

Now it is time to write the code that will make the first test pass. Let’s flesh out our smart contract a bit.

pragma solidity ^0.4.17;
contract FundRaise {
    address public owner;
    // @dev constructor function. Sets contract owner 
    function FundRaise() {
        owner = msg.sender;
    }
}

Trigger the test again, i.e.truffle test test/fundRaise.js :

Contract: FundRaise
    ✓ has an owner (41ms)
1 passing (138ms)

Great! Let’s move on and add the next one.

const FundRaise = artifacts.require('./FundRaise.sol')
  contract('FundRaise', function ([owner, donor]) {
    let fundRaise
    beforeEach('setup contract for each test', async function () {
      fundRaise = await FundRaise.new(owner)
    })
    it('has an owner', async function () {
      assert.equal(await fundRaise.owner(), owner)
    })
    it('is able to accept funds', async function () {
      await fundRaise.sendTransaction({ value: 1e+18, from: donor })
      const fundRaiseAddress = await fundRaise.address
      assert.equal(web3.eth.getBalance(fundRaiseAddress).toNumber(), 1e+18)
    })
})

The error this time is:

1 failing
1) Contract: FundRaise is able to accept funds:
     Error: VM Exception while processing transaction: invalid opcode

Right, we need to let our contract receive Ether. Let’s fix that.

pragma solidity ^0.4.17;
contract FundRaise {
    address public owner;
// @dev constructor function. Sets contract owner 
    function FundRaise() {
        owner = msg.sender;
    }
    
    // fallback function that allows contract to accept ETH 
    function () payable {}
}

And the result is:

Contract: FundRaise
    ✓ has an owner (38ms)
    ✓ is able to accept funds (234ms)
2 passing (473ms)

Beautiful. This is the process one goes through systematically to cover the smart contracts with test written in JavaScript. Just keep doing this process until all smart contract specifications are met.

For simplicity sake, I am going to fast forward the process and add the complete set of tests for the FundRaise contract so you have an idea how it would look like in the end.

Note the tests for the pause/unpause and owner receive funds tests

and here is the full code for the smart contract:

Run the tests one last time…

Contract: FundRaise
    ✓ has an owner (46ms)
    ✓ accepts funds (193ms)
    ✓ is able to pause and unpause fund activity (436ms)
    ✓ permits owner to remove funds (653ms)
4 passing (2s)

Marvelous!

Hope you learned a thing or two about testing smart contracts with JavaScript and its use in blockchain development. Now carry on and keep testing the heck out of your smart contracts.

The code for this blog post can be found here: https://github.com/gustavoguimaraes/smart-contract-testing-javascript-example-

原文地址: https://medium.com/@gus_tavo_guim/testing-your-smart-contracts-with-javascript-40d4edc2abed

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值