Writing our first contract
Let's take our first steps into writing Solidity by test driving a Fizzbuzz contract. Our Fizzbuzz function should take an integer argument and:
- Return "fizz" for numbers divisible by 3
- Return "buzz" for numbers divisible by 5
- Return "fizzbuzz" for numbers divisible by both 3 and 5
- Return the input number as a string for all other cases.
Let's start by setting up a new test contract. Create FizzBuzz.t.sol
alongside Greeter.t.sol
:
$ touch src/test/FizzBuzz.t.sol
We'll start with a test that intentionally fails in order to make sure everything's connected correctly:
// SPDX-License-Identifier: Apache-2.0
pragma solidity 0.8.10;
import "ds-test/test.sol";
import "../FizzBuzz.sol";
contract FizzBuzzTest is DSTest {
FizzBuzz internal fizzbuzz;
function setUp() public {
fizzbuzz = new FizzBuzz();
}
function test_math_is_broken() public {
uint256 two = 1 + 1;
assertEq(two, 3);
}
}
Function names
A brief style note: it's conventional to use
mixedCase
for Solidity function and variable names, but I like to intentionally break this rule for test functions and usesnake_case
instead. This helps distinguish them from production code when reading test output and function traces.
Let's give our newly created tests a spin:
$ forge test
[⠊] Compiling...
[⠒] Unable to resolve import: "../FizzBuzz.sol" with remappings:
ds-test/=/Users/ecm/Projects/forge-template/lib/ds-test/src/
forge-std/=/Users/ecm/Projects/forge-template/lib/forge-std/src/
openzeppelin-contracts/=/Users/ecm/Projects/forge-template/lib/openzeppelin-contracts/
src/=/Users/ecm/Projects/forge-template/src/
[⠆] Compiling 2 files with 0.8.10
[⠰] Solc finished in 9.06ms
Error:
0: Compiler run failed
ParserError: Source "/Users/ecm/Projects/forge-template/src/FizzBuzz.sol" not found: File not found.
--> /Users/ecm/Projects/forge-template/src/test/FizzBuzz.t.sol:5:1:
|
5 | import "../FizzBuzz.sol";
| ^^^^^^^^^^^^^^^^^^^^^^^^^
0:
Location:
cli/src/cmd/utils.rs:43
Oops, we forgot to create our contract under test! Fortunately, the Solidity compiler outbut is usually very helpful. Here, it points to the location of the missing import, and prints the full path to the file it can't find. Let's create FizzBuzz.sol
and try again:
$ touch src/FizzBuzz.sol
For now, we'll just create an empty contract:
// SPDX-License-Identifier: Apache-2.0
pragma solidity 0.8.10;
contract FizzBuzz {}
With our empty production contract in place, let's try again:
forge test
[⠊] Compiling...
[⠢] Compiling 2 files with 0.8.10
[⠆] Solc finished in 32.87ms
Compiler run successful
Running 1 test for src/test/FizzBuzz.t.sol:FizzBuzzTest
[FAIL] test_math_is_broken() (gas: 10940)
Logs:
Error: a == b not satisfied [uint]
Expected: 3
Actual: 2
Test result: FAILED. 0 passed; 1 failed; finished in 1.49ms
Failed tests:
[FAIL] test_math_is_broken() (gas: 10940)
Encountered a total of 1 failing tests, 0 tests succeeded
Success! Fortunately, math is not actually broken, and our test failed as it should. Forge printed the expected and actual values to help us diagnose the failure.