Solidity unit testing using remix-tests -part 2

Custom compiler-transaction context & use as a CLI-CI

Aniket
Remix Project

--

This blog is in continuation of a previous one. It is recommended to read part 1 first.

In part 1, we learnt how to write tests for Solidity smart contract in Solidity itself using remix-tests .

Although contracts frequently need to be tested in different transaction contexts:

  • with different values for sender account, gas and value
  • with different version of compiler & EVM
  • with enabled/disabled optimization while compilation

The first half of this blog post, describes how can one write tests with different contexts and in second half, we will talk about using remix-tests as CLI-CI.

Custom Compiler Context

If you are looking to pass custom compiler options for your test contract, you can use the Solidity Compiler plugin to set the options.

Solidity Compiler Plugin

You can select the required Compiler, EVM Version and Enable optimization for your contract. These selections will be used for the compilation of test contract.

Custom Transaction Context

For a method call, main parameters are sender, value & gas.

You can set the sender and value for a transaction which executes the method. This can be done using Solidity NatSpec as :

import "remix_tests.sol"; 
import "remix_accounts.sol"; <--- Notice this import
contract SenderAndValueTest {
function beforeAll () public {}
/// #sender: account-1
function checkSenderIs1 () public {
Assert.equal(msg.sender, TestsAccounts.getAccount(1), "wrong sender in checkSenderIs1");
}
/// #sender: account-0
/// #value: 10

function checkSenderIs0AndValueis10 () public payable{
Assert.equal(msg.sender, TestsAccounts.getAccount(0), "wrong sender in checkSenderIs0AndValueis10");
Assert.equal(msg.value, 10, "wrong value in checkSenderIs0AndValueis10");
}
/// #value: 100
function checkValueIs100 () public payable{
Assert.equal(msg.value, 100, "wrong value in checkValueIs100");
}
}

To use custom sender functionality, you have to import remix_accounts.sol in your test file as in above file.

Note: A test file which imports remix_accounts.sol might not compile successfully with theSolidity Compiler plugin but it will work fine with Solidity Unit Testing plugin

Regarding gas, we estimate the required gas for each transaction internally. Still if a contract deployment fails with ‘Out-of-Gas’ error, we try to redeploy it by doubling the gas internally. Deployment failing with double gas will show error. Custom gas for a method inside test contract is not possible for now.

Having the ability to easily set the sender and the value is especially important when the contract being tested involve multiple accounts each of which can have multiple roles.

Remix-tests as a CLI

remix-tests can be installed as a global NPM package and can be used as a command line module. As a CLI, this can be used to run a test file or all the test files from a directory using a local file system.

  • To run all test files (suffixed with _test.sol) inside examples directory
$ remix-tests examples/
  • To run single test file named simple_storage_test.sol inside examples directory
$ remix-tests examples/simple_storage_test.sol

Let’s use the same contract (from last post) and run it locally using remix-tests :

simple_storage.sol: contract to be tested

pragma solidity >=0.4.22 <0.7.0;

contract SimpleStorage {
uint public storedData;

constructor() public {
storedData = 100;
}

function set(uint x) public {
storedData = x;
}

function get() public view returns (uint retVal) {
return storedData;
}
}

simple_storage_test.sol: test contract file (Assert library documentation)

pragma solidity >=0.4.22 <0.7.0;
import "remix_tests.sol";
import "./simple_storage.sol";

contract MyTest {
SimpleStorage foo;

function beforeEach() public {
foo = new SimpleStorage();
}

function initialValueShouldBe100() public returns (bool) {
return Assert.equal(foo.get(), 100, "initial value is not correct");
}

function valueIsSet200() public returns (bool) {
foo.set(200);
return Assert.equal(foo.get(), 200, "value is not 200");
}

function valueIsNotSet200() public returns (bool) {
return Assert.notEqual(foo.get(), 200, "value is 200");
}
}

Running test contract file as:

$ remix-tests simple_storage_test.sol

will run tests as:

◼  MyTest
[19:15:02] payload method is eth_gasPrice
[19:15:02] payload method is eth_sendTransaction
[19:15:02] payload method is eth_getTransactionReceipt
[19:15:02] payload method is eth_gasPrice
[19:15:02] payload method is eth_sendTransaction
[19:15:02] payload method is eth_getTransactionReceipt
✓ Initial value should be100
[19:15:02] payload method is eth_gasPrice
[19:15:02] payload method is eth_sendTransaction
[19:15:02] payload method is eth_getTransactionReceipt
[19:15:02] payload method is eth_gasPrice
[19:15:02] payload method is eth_sendTransaction
[19:15:02] payload method is eth_getTransactionReceipt
✓ Value is set200
[19:15:02] payload method is eth_gasPrice
[19:15:02] payload method is eth_sendTransaction
[19:15:02] payload method is eth_getTransactionReceipt
[19:15:02] payload method is eth_gasPrice
[19:15:02] payload method is eth_sendTransaction
[19:15:02] payload method is eth_getTransactionReceipt
✓ Value is not set200

This feature of remix-tests can help to test the contract with continuous Integration. See this script example and related Travis CI build.

Note: Custom Transaction Context described above also works with CLI usage of remix-tests

Developers can use remix-tests as a library. This use is well defined here.

Hope both the blogs have explained it well and now you are good to go with the testing of your different contracts using remix-tests .

So, this is it for usage explanation. There is a third part of ‘Solidity unit testing using remix-tests’ series which provides some code examples to give you a push for development.

Remix team is continuously working to make remix-tests feature rich and helpful to devs. Let us know your feedback/query/suggestion on Remix gitter channel.

Thanks for reading!

--

--