Reading the tests
Foundry uses an xUnit style test framework called ds-test
, written in Solidity. Unlike other Solidity development frameworks, this makes it possible to write pure Solidity unit tests.
Test files are created as .sol
files in the src/test/
directory. It's a convention to put an extra .t
in the test file name, such that Greeter.t.sol
is the test file for the Greeter.sol
contract. (However, this is just a convention. The test runner will recognize as tests any files that inherit from ds-test
).
Let's take a look at our test file, Greeter.t.sol
. We haven't covered Solidity syntax in detail yet, but hopefully it will be legible enough to make some observations:
// SPDX-License-Identifier: Apache-2.0
pragma solidity 0.8.10;
import "ds-test/test.sol";
import "forge-std/stdlib.sol";
import "forge-std/Vm.sol";
import "../Greeter.sol";
contract GreeterTest is DSTest {
Vm public constant vm = Vm(HEVM_ADDRESS);
Greeter internal greeter;
function setUp() public {
greeter = new Greeter("Hello");
}
function test_default_greeting() public {
assertEq(greeter.greet(), "Hello, world!");
}
function test_custom_greeting() public {
assertEq(greeter.greet("foundry"), "Hello, foundry!");
}
function test_get_greeting() public {
assertEq(greeter.greeting(), "Hello");
}
function test_set_greeting() public {
greeter.setGreeting("Ahoy-hoy");
assertEq(greeter.greet(), "Ahoy-hoy, world!");
}
function test_non_owner_cannot_set_greeting() public {
vm.prank(address(1));
try greeter.setGreeting("Ahoy-hoy") {
fail();
} catch Error(string memory message) {
assertEq(message, "Ownable: caller is not the owner");
}
}
}
Our test suite is defined as a Solidity contract, which looks a lot like a class or module in other languages.
contract GreeterTest is DSTest {
}
We create an instance of the contract we're testing in the setUp
function, and access it later in our tests:
function setUp() public {
greeter = new Greeter("Hello");
}
Each test method is a public function prefixed with the word test
.
function test_default_greeting() public { }
function test_custom_greeting() public { }
function test_get_greeting() public { }
function test_set_greeting() public { }
Inside each of these functions, we have access to assertions like assertEq
:
function test_default_greeting() public {
assertEq(greeter.greet(), "Hello, world!");
}
If you've used an xUnit style test framework before, this should all be pretty familiar. In fact, with the exception of a few keywords, this looks a lot like Java or Javascript.