3 min read

Primitives Tests - Foundry

Overview

If you prefer to use Foundry, follow along and learn how to use the tooling. If you have not installed and configured Forge on your machine, follow this guide: Foundry Configuration.

  1. First let's create a folder to hold our tests and lessons. Navigate to the directory (folder) of your choice. Then, create a new directory.
forge init Solidity101-Forge
  1. Next, if you'd like to remove the default project, run the following command. If not, make sure to inspect the simple Country.sol smart contract and its associated files.
rm contracts/Counter.sol test/Counter.t.sol script/Counter.s.sol
  1. Lets create the files we will be using
touch src/Primitives.sol test/Primitives.t.sol script/Primitives.s.sol
  1. Now, copy and paste the the same contract we have seen before: contracts/Primitives.sol.
// SPDX-License-Identifier: MIT
// Primitives.sol
pragma solidity ^0.8.16;

contract Primitives {
  // public variables have automatic getter functions
  bool public storedBool;  // default: false
  int public storedInt;    // default: 0 
  uint public storedUint;  // default: 0
  address public storedAddress; // default address: 0x0000000000000000000000000000000000000000
  bytes public storedBytes;     // default: 0x
  string public storedString;   // default: empty stiring aka ""
  mapping(address => uint) public balances;   // 0x0..00 address => 0 uint

  enum State { 
		PENDING, 
		APPROVED, REJECTED 
	} // creates enum structure
	
  State public state; // creates instance of enum
}
  1. Once place where Hardhat differs from Foundry is in its tests. Tests in Foundry can be done with Solidity itself, allowing for more optimizations. Copy and paste the following code to test/Primitives.t.sol.
// Import the console library for debugging purposes
import "forge-std/console.sol";

// Import the DSTest library for testing
import "ds-test/test.sol";

// Import the Primitives contract we want to test
import "../src/Primitives.sol";

// Define a contract that inherits from DSTest
contract PrimitivesTest is DSTest {
    // Declare a Primitives contract instance variable
    Primitives primitives;

    // Define a setUp function to create a new instance of Primitives before each test
    function setUp() public {
        primitives = new Primitives();
    }

    // Define a testBool function to test the storedBool variable
    function testBool() public {
        assertTrue(!primitives.storedBool());
    }

    // Define a testInt function to test the storedInt variable
    function testInt() public {
        assertEq(primitives.storedInt(), 0);
    }

    // Define a testUint function to test the storedUint variable
    function testUint() public {
        assertEq(primitives.storedUint(), 0);
    }

    // Define a testAddress function to test the storedAddress variable
    function testAddress() public {
        assertEq(primitives.storedAddress(), address(0));
    }

    // Define a testString function to test the storedString variable
    function testString() public {
        assertEq(primitives.storedString(), "");
    }

    // Define a testEnim function to test the State enum
    function testEnim() public {
        assertEq(uint(primitives.state()), 0);
    }

    // Define a testBytes function to test the storedBytes variable
    function testBytes() public {
        // Convert the storedBytes variable to a string
        string memory byteString = string(abi.encodePacked(primitives.storedBytes()));
      
        // Log the byteString to the console for debugging purposes
        console.log("bytestring %s", byteString);

        // Check that the byteString is empty
        assertEq(string(byteString), "");
    }

    // Define a testMapping function to test the balances mapping
    function testMapping() public {
        assertEq(primitives.balances(address(0)), 0);
    }
}

To run our tests run the following command:

forge test -vv

Note: that you can pass from 1 to 5 vs in as flags each with increasing logging output in verbosity. See Foundry Configuration for more explanation or the Foundry manual.

The output should look something like this:

[⠢] Compiling...
[⠒] Compiling 1 files with 0.8.19
[⠑] Solc 0.8.19 finished in 271.14ms
Compiler run successful (with warnings)
warning[3420]: Warning: Source file does not specify required compiler version! Consider adding "pragma solidity ^0.8.19;"
--> test/Primitives.t.sol




Running 8 tests for test/Primitives.t.sol:PrimitivesTest
[PASS] testAddress() (gas: 7777)
[PASS] testBool() (gas: 7693)
[PASS] testBytes() (gas: 12791)
[PASS] testEnim() (gas: 7695)
[PASS] testInt() (gas: 7608)
[PASS] testMapping() (gas: 7788)
[PASS] testString() (gas: 9243)
[PASS] testUint() (gas: 7652)
Test result: ok. 8 passed; 0 failed; finished in 493.42µs

Running 2 tests for test/Counter.t.sol:CounterTest
[PASS] testIncrement() (gas: 28334)
[PASS] testSetNumber(uint256) (runs: 256, μ: 27242, ~: 28409)

Foundry Test Explained

The tests themselves are fairly simple. Each test calls a function in the Primitives contract that returns the value of a particular variable, and then uses an assertion to check that the value is equal to the expected default value (which is zero for numeric types, an empty string for strings and bytes, and the zero address for addresses).

The testBytes function is slightly more complicated, since the bytes type doesn't have a string representation. To test the storedBytes variable, we convert it to a string using the abi.encodePacked function, and then use the console.log function from the forge-std library to log the value to the console for debugging purposes. We then check that the resulting string is empty.

To run these tests using Foundry, you can compile the Primitives contract and then run the tests using the following command:

foundry test

This will compile the Primitives contract and the PrimitivesTest test contract, and then run all of the tests in the PrimitivesTest contract.

Feedback

Have feedback? Found something that needs to be updated, accurate, or explained?

Join our discord and share what can be improved.

Any and all feedback is welcomed.