We are going to write tests for a simple counter contract:
Overwrite Counter.sol with this contract:
// SPDX-License-Identifier: MITpragmasolidity ^0.8.17;contract Counter {uintpublic count;// Function to get the current countfunctionget() publicviewreturns (uint) {return count; }// Function to increment count by 1functioninc() public { count +=1; }// Function to decrement count by 1functiondec() public {// This function will fail if count = 0 count -=1; }}
Write test file Counter.t.sol:
// SPDX-License-Identifier: UNLICENSEDpragmasolidity ^0.8.13;import"forge-std/Test.sol";import"../src/Counter.sol";contractCounterTestisTest { Counter public counter;functionsetUp() public { counter =newCounter(); }functiontestInc() public { counter.inc();assertEq(counter.count(),1); }}
Test for error
Let's write a test case that fails. Note that setup() will be executed before executing each test case, so the counter contract in the new test case is brand new. It has nothing to do with the operations we have done in the former test case testInc().
functiontestFailDec() public { counter.dec();}
This new test case shows "PASS" because the testFail prefix tests for failure. A failed testFail case will pass.
vm.expectRevert
We can also specify expected revert reason in the test case:
functiontestDecUnderflow() public { vm.expectRevert(stdError.arithmeticError); counter.dec();}
In this case we are testing for integer underflow.
Add one more test case:
functiontestDec() public { counter.inc(); counter.inc(); counter.dec();assertEq(counter.count(),1);}