Solidity unit testing using remix-tests -part 2
Custom compiler-transaction context & use as a CLI-CI
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.
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 importcontract 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
) insideexamples
directory
$ remix-tests examples/
- To run single test file named
simple_storage_test.sol
insideexamples
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!