▶️ Watch on YouTube
Foundry made its debut in 2022. Before this, Solidity was rarely used for testing smart contracts.
Today, many Foundry test suites are disorganized
function test_Foo() external {
uint256 x = 42;
assertEq(contract.foo(x), x, "value mismatch");
}
function test_Foo_1() external {
// --- snip ---
}
function test_Foo_2() external {
// --- snip ---
}
function test_Foo_3(uint256 arg0) external {
// --- snip ---
}
function test_Foo_4(uint256 arg0, uint256 arg1) external {
// --- snip ---
}
Create a directory for each of the following categories:
Category | Description |
---|---|
Unit | Functions involving a single contract |
Integration | Function involving multiple contracts |
Invariant | Expressions that should always hold true |
Fork | Tests running against a production chain |
Sub-category | Description |
---|---|
Concrete | Standard deterministic tests that take no inputs |
Fuzz | Non-deterministic tests with fuzzed inputs |
test_Foo
testFuzz_Foo
test_RevertWhen_Foo
testFuzz_RevertWhen_Foo
statusOf.tree
├── when the id references a null stream
│ └── it should revert
└── when the id does not reference a null stream
├── given assets have been fully withdrawn
│ └── it should return DEPLETED
└── given assets have not been fully withdrawn
├── given the stream has been canceled
│ └── it should return CANCELED
└── given the stream has not been canceled
├── given the start time is in the future
│ └── it should return PENDING
└── given the start time is not in the future
├── given the refundable amount is zero
│ └── it should return SETTLED
└── given the refundable amount is not zero
└── it should return STREAMING
statusOf
function test_RevertWhen_Null() external {
uint256 nullStreamId = 1729;
vm.expectRevert(abi.encodeWithSelector(Errors.SablierV2Lockup_Null.selector, nullStreamId));
lockup.statusOf(nullStreamId);
}
modifier whenNotNull() {
defaultStreamId = createDefaultStream();
_;
}
function test_StatusOf()
external
whenNotNull
givenAssetsNotFullyWithdrawn
givenStreamNotCanceled
givenStartTimeNotInTheFuture
givenRefundableAmountNotZero
{
vm.warp({ timestamp: defaults.START_TIME() + 1 seconds });
Lockup.Status actualStatus = lockup.statusOf(defaultStreamId);
Lockup.Status expectedStatus = Lockup.Status.STREAMING;
assertEq(actualStatus, expectedStatus);
}
Technique | Barrier to entry | Effectiveness |
---|---|---|
Braching Tree Technique | Entry-level | Moderately effective |
Cucumber Gherkin | Medium-level | Moderately effective |
Certora, TLA+ | Senior-level | Highly effective |