Why I’m Writing Tests in Solidity with Foundry

Over the last few weeks, I’ve been using Foundry for smart contract development instead of Truffle or Hardhat. Foundry is the new kid on the block but has quickly garnered popularity due to being a Rust implementation of dapp tools.

It’s marketed as a fast and flexible way to develop and test Solidity contracts. Coming from a largely Javascript background, I wanted to improve my Solidity skills and write all my tests in Solidity.

While I won’t cover all of the reasons devs should give Foundry a shot (more info here), I’ll talk about my experience thus far.

The Cool Stuff

One of the tools provided by Foundry is Forge, an Ethereum testing framework! Outside of simple unit tests, Forge allows you to fuzz test, manipulate blockchain state via “cheat codes,” and produce gas reports.

Code time

Below is an example of testing an NFT contract.

// SPDX-License-Identifier: MIT
 
pragma solidity ^0.8.7;
 
import "@std/Test.sol";
import "./utils/Cheats.sol";
import "../MyNFT.sol";
 
contract TestSomeContract is Test {
   address public owner;
   address bob = address(0x12345);
  
   SpritelyNFT public spritelyNFT;
 
   function setUp() public {
       owner = msg.sender;
       vm.label(address(this), "Spritely test contract");
       myNFT = new MyNFT();
       vm.deal(address(bob), 1000 ether);
   }
 
   function invariantMetaData() public {
       assertEq(myNFT.name(), "MyNFT");
       assertEq(myNFT.symbol(), "MFT");
   }
 
   function testCanMintToken() public {
       myNFT.setSaleIsActive(true);
       vm.prank(address(bob));
       myNFT.mint{value: 30000000 gwei}(1);
       assertEq(myNFT.balanceOf(address(bob)), 1);
   }
}

This test file looks remarkably like a smart contract!

To run my tests I use: forge test

And if there are failures or I’m debugging, I can use the flags -vvv to show traces for failed tests or -vvvv for all tests.

By instantiating my contract in the setup, I can now thoroughly test its functionality.

In this example, the mint function requires that saleIsActive == true, and the setter function is only callable by the contract owner.

To get around this, I’ve set the msg.sender as owner in setup, but I could just as easily transfer ownership to a mocked address such as bob within the test. To each their own…

By leveraging Foundry’s cheat codes via vm.___ I’m able to mock the balance of Bob’s address and then call `mint()` as if I were them!

Final Thoughts

Overall, I’ve enjoyed working with Foundry, and my knowledge of the EVM has continued to grow through this process. While it may be the new kid on the block, it has strong community backing and solid development. There are many more features that I have yet to leverage, but I’ll continue to work them into my practices when appropriate.

At the moment, when it comes to testing most NFT smart contracts, Foundry feels like overkill. However, for more complicated systems such as defi protocols, it seems ideal.

Most resources on testing smart contracts are Javascript-based, so at times it can be difficult to find helpful guides or information. For those who’d like to go in-depth with Foundry, here are my three favorite resources:
Intro to Foundry | The FASTEST Smart Contract Framework
Foundry Book
Smart Contract Development with Foundry

Have you used Foundry? What’s your opinion on the framework? Let me know below!