Writing Tests
Tests are written in Solidity. If the test function reverts, the test fails, otherwise it passes.
Let's go over the most common way of writing tests, using the Forge Standard Library's Test
contract, which is the preferred way of writing tests with Forge.
In this section, we'll go over the basics using the functions from the Forge Std's Test
contract, which is itself a superset of DSTest. You will learn how to use more advanced stuff from the Forge Standard Library soon.
DSTest provides basic logging and assertion functionality. To get access to the functions, import forge-std/Test.sol
and inherit from Test
in your test contract:
import "forge-std/Test.sol";
Let's examine a basic test:
pragma solidity 0.8.10;
import "forge-std/Test.sol";
contract ContractBTest is Test {
uint256 testNumber;
function setUp() public {
testNumber = 42;
}
function testNumberIs42() public {
assertEq(testNumber, 42);
}
function testFailSubtract43() public {
testNumber -= 43;
}
}
Forge uses the following keywords in tests:
setUp
: An optional function invoked before each test case is run.function setUp() public { testNumber = 42; }
test
: Functions prefixed withtest
are run as a test case.function testNumberIs42() public { assertEq(testNumber, 42); }
testFail
: The inverse of thetest
prefix - if the function does not revert, the test fails.
A good practice is to use the patternfunction testFailSubtract43() public { testNumber -= 43; }
test_Revert[If|When]_Condition
in combination with theexpectRevert
cheatcode (cheatcodes are explained in greater detail in the following section). Also, other testing practices can be found in the Tutorials section. Now, instead of usingtestFail
, you know exactly what reverted and with which error:function testCannotSubtract43() public { vm.expectRevert(stdError.arithmeticError); testNumber -= 43; }
Tests are deployed to 0xb4c79daB8f259C7Aee6E5b2Aa729821864227e84
. If you deploy a contract within your test, then
0xb4c...7e84
will be its deployer. If the contract deployed within a test gives special permissions to its deployer,
such as Ownable.sol
's onlyOwner
modifier, then the test contract 0xb4c...7e84
will have those permissions.
⚠️ Note
Test functions must have either
external
orpublic
visibility. Functions declared asinternal
orprivate
won't be picked up by Forge, even if they are prefixed withtest
.
Shared setups
It is possible to use shared setups by creating helper abstract contracts and inheriting them in your test contracts:
abstract contract HelperContract {
address constant IMPORTANT_ADDRESS = 0x543d...;
SomeContract someContract;
constructor() {...}
}
contract MyContractTest is Test, HelperContract {
function setUp() public {
someContract = new SomeContract(0, IMPORTANT_ADDRESS);
...
}
}
contract MyOtherContractTest is Test, HelperContract {
function setUp() public {
someContract = new SomeContract(1000, IMPORTANT_ADDRESS);
...
}
}
💡 Tip
Use the
getCode
cheatcode to deploy contracts with incompatible Solidity versions.