1

In the following example, I want to test the assignVoter functionality with Remix unit test plugin. assignVoter should only be called by the owner.

pragma solidity >=0.4.22 <0.7.0;

contract Ballot {
    address owner;
    
    constructor() public {
        owner = msg.sender;    
    }
    
    function assignVoter(address voter) public {
        require(msg.sender == owner);
    }
}
pragma solidity >=0.4.22 <0.7.0;
import "remix_tests.sol";
import "remix_accounts.sol";
import "../Ballot.sol";

contract BallotTest {
    Ballot private ballot;

    /// #sender: account-0
    function beforeAll() public {
        ballot = new Ballot();
    }

    /// #sender: account-1
    function testAssignVoter() public payable {
        ballot.assignVoter(TestsAccounts.getAccount(1));
    }
}

Remix provides the functionality of custom msg.sender of transaction using devdoc (NatSpec). In this example, such an annotation makes the unit testing framework call the testAssignVoter method with 0-th test account. In turn, the creation of the Ballot contract and the call to assignVoter --- both are made by the BallotTest contract; neither by account-0 or account-1. Therefore, I am not being able to simulate the situation where the Ballot contract is created by one account (account-0), and the assignVoter is called by another (account-1). Specifically, I want to simulate the failure scenario by hitting the require predicate. How can I achieve that?

1
  • I updated the code snippet, the previous one was not compilable. The test passes.
    – sherlock
    Nov 1, 2020 at 16:05

1 Answer 1

2

To resemble multiple user functionality, you have to update your test file a bit with inheritance like:

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

contract BallotTest is Ballot {

    /// #sender: account-1
    function testAssignVoter() public payable {
       assignVoter(TestsAccounts.getAccount(1));
    }
}

This way your test will fail as account-1 is not owner. You can test same with a passing test case using try-catch. Although it is for external method call, you can use this to call method from same contract (Don't do that apart from a test contract). First add error message in require like: require(msg.sender == owner, 'not an owner');

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

contract BallotTest is Ballot {

    /// #sender: account-1
    function testAssignVoterFailureUsingTryCatch() public {
        try this.assignVoter(TestsAccounts.getAccount(1)){
            Assert.ok(false, 'method execution should fail');
        } catch Error(string memory reason) {
            // Compare failure reason, check if it is as expected
            Assert.equal(reason, 'not an owner', 'failed with unexpected reason');
        } catch (bytes memory /*lowLevelData*/) {
            Assert.ok(false, 'failed unexpected');
        }
    }
}

This can help to test multiple require method failure from a single method.

For more test examples: https://remix-ide.readthedocs.io/en/latest/unittesting_examples.html

3
  • 1
    When you use this doesn't the assignVoter function gets called with msg.sender set to contract address than address of account-1? Jun 6, 2021 at 3:46
  • No, it is updated in the transaction.
    – Aniket
    Jun 7, 2021 at 13:42
  • @Aniket I tried this and the assignVoter function gets called as from the contract address (this), not account-1
    – Jonathan
    Sep 22 at 6:49

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.