Foundry for Beginners: A Smart Contract Testing Deep Dive

Analyzing_part2_13

Web3 development has recently become a strong trend, capturing the attention of many Web2 developers. However, these new developers, especially those working at companies looking to integrate smart contracts into their products, may inadvertently introduce logic and security errors.

One of the main reasons for these errors is a lack of thorough testing or reliance on tools that don’t fully leverage Solidity’s capabilities, like Hardhat. While Hardhat is a useful tool, it doesn’t always maximize Solidity’s potential for efficient and secure smart contract development.

At Vottun, we’ve chosen Foundry for our smart contract testing. If you’re new to Web3 development, you might not be familiar with it, but Foundry is a crucial tool to consider.

This article is aimed at newer Solidity developers, so I won’t dive too deep into the technicalities of Foundry. In a nutshell, Foundry is a tool that lets you write tests using Solidity itself – a huge advantage because you can use the same language for both development and testing. It also provides powerful features to simulate various environments, scenarios, and conditions, making your smart contracts more robust.

First of all let’s see how to start creating tests in Solidity using Foundry. You will need a function in your contract file that looks like this:

The key point here is that the function name must be setUp. Foundry’s compiler recognizes this special function and will execute everything within it before each individual test case.

Defining tests is then quite simple:

You’ll need to define your test function names like this: test_WhateverYouWant(). Foundry recommends some best practices for naming, which you can find here. Naming convention is important because as you write more tests, you’ll need to quickly understand what each test does and whether a failure indicates a bug or expected behavior.

One of the most frequently used functionalities is vm.startPrank(owner); and vm.stopPrank();.

First, you’ll create an address using vm.addr(“ADMIN”); to use with vm.startPrank(createdAddress). Place all the function calls and logic you want to test within your smart contract between startPrank and stopPrank. This way, all the calls will be made as if they were from the created account. Without startPrank and stopPrank, calls are made using a default account that Foundry automatically provides.

If you only want to test one specific function, you can use vm.prank(createdAddress); directly before the function call you’re testing.

Another function you’ll likely use frequently is vm.warp(targetTime). This function allows you to manipulate the blockchain’s timestamp, fast-forwarding time to test features in your smart contracts that depend on time passing, such as staking lock-up periods.

You can get the current timestamp in seconds using uint256 currentTime = block.timestamp;

Note that we use block.timestamp directly, as it returns the timestamp in seconds. The vm.unixTime() function, while available, returns the time in milliseconds.

So if you want to do comparisons between dates, you will have to keep this in mind: the underscore in a number like ‘1_000’ is just a way to define numbers in Solidity for visual clarity. The underscore itself doesn’t change the value of the number.

Finally, once you’ve written your tests, simply execute forge test -vv. The -vv parameter enables verbose output, which is useful if you have console.log statements in your tests and want to see their results. For more information on Foundry commands, refer to the Foundry Book.

These are some of the most basic functionalities of Foundry. It might seem straightforward, but as you start writing tests, you’ll discover many more features that can help you thoroughly test your contracts and explore various scenarios.