Hardhat – Best Practices for Writing Tests Using Chai in Solidity

chaihardhatsoliditytesting

I've digged through all the related question, but none of these worked for me.
I used this repo, and replaced the contract and test file : https://github.com/NomicFoundation/hardhat-boilerplate

EDIT 26/11 : was able to reproduce it with a simple modification from the repo above, if you want to try it yourself :
https://github.com/NomicFoundation/hardhat-boilerplate/issues/79

I'll start by giving the context of the issue, then I'll present my issue.

Context description

the contract is a multi-sig wallet with approvers defined at construction.
I get signers here, and then deploy my contract where I set signer0..2 as approvers :

const [signer0, signer1, signer2, signer3, signer4] = await ethers.getSigners();
const wallet = await walletFactory.deploy([signer0.address, signer1.address, signer2.address]);

I'm testing this solidity function :

    function createTransfer (uint amount, address payable to) external onlyApprover() {
    transfers.push(Transfer(
        transfers.length,
        amount,
        to,
        0,
        false)
    );
}

which has a modifier onlyApprover :

    modifier onlyApprover() {
    bool allowed = false;
    for (uint i=0; i<approvers.length; i++) {
        if (approvers[i] == msg.sender) {
            allowed = true;
        }
    }
    require(allowed == true, "only approver allowed");
    _;
}

When calling this function with an account which is not part of the approvers array, it reverts.

Issue

 await expect(wallet.connect(signer3).createTransfer(100, addr4, {from: signer3.address}));

OR

await expect(wallet.connect(signer3).createTransfer(100, addr4, {from: signer3.address})
    ).to.be.reverted;

The test pass while it should pass only for the second one

If I put the await inside the expect :

 expect(await wallet.connect(signer3).createTransfer(100, addr4, {from: signer3.address}));

OR

expect(await wallet.connect(signer3).createTransfer(100, addr4, {from: signer3.address})
    ).to.be.reverted;

The test do not pass in both cases with this error, which is the solidity error I expect !.. but it should be caught by the test and validate it in the to.be.reverted case.

Error: VM Exception while processing transaction: reverted with reason string 'only approver allowed'

I also tried using to.be.revertedWith('only approver allowed'), same issue in both case.

questions

What am I missing ?
Does someone have a repo to share where I could see something like that implemented and working ?

Thank you

Best Answer

I got an answer from a member of this repo which explain everything :

If you do await expect(...something...), the promise returned inside the expect will be captured by Chai. If you then don't assert anything on that promise, then the result will be discarded and the test will pass.

I wasn't aware of that, now I found how to verify that a transaction is NOT reverted (what I was looking for), I need to use .not.to.be.reverted : https://www.chaijs.com/api/bdd/#method_not

Related Topic