How to start testing your smart contracts with Solidity

Note: In this article I reference Dapptools, however, now recommend Foundry. Luckily, Foundry also uses ds-test and is similar to Dapptools. Most of what is written here applies to Foundry. The only difference is the terminal commands, which you can find here.

Introduction

Testing your smart contracts is a key step before you deploy.

Deploying untested code risks sending bugs to Mainnet that could be easily exploitable by a hacker and damaging to your end-user.

It’s common to test your Solidity smart contracts in another programming language (like Javascript).

This may work for some, but what if you deeply dislike Javascript? (sorry JS lovers 😉)

What if you’d prefer to spend more time coding in Solidity? 🖐

Well, the good news is you can.

The development framework Dapptools provides a testing library called ds-test that allows you to write your tests in Solidity.

Below, I review the advantages of testing in Solidity and offer some examples to help you get started.

Why write tests in Solidity

Some love Javascript or Python. I love Solidity.

And I hate switching from Solidity to another language.

Any day I get to spend my time just coding in Solidity is a good day for me 🙌

But there is more to using ds-test than just preference.

When you write tests in Solidity, it’s easy to switch between contract and test functions as you write them because you are still working in the same programming context.

I find this streamlines my development and makes me a more effective programmer.

Plus when I only have to worry about a single language, I find myself more excited to code, and it’s easier to get into a flow state.

Of course, some disagree.

Switching to another language to write tests could force you to provide better testing coverage and improve how you design the contract.

But ultimately, it comes down to personal preference and whatever helps you build the most secure and efficient smart contracts possible.

Do what works best for you!

How to approach testing

Testing is always important.

It’s even more imperative when we’re talking about sending code to the blockchain.

Why?

Because once your code is deployed to the blockchain it cannot be changed.

So, we need to make sure that we’re able to predict how other users and contracts will interact with our smart contract.

What are some specific advantages to testing your Solidity code?

  • It ensures that the contract is initialized properly and works as expected.
  • Allows for the automation of contract interactions. This way you don’t have to think of every possibility manually.
  • Allows you to check that functions provide expected output with various different inputs.
  • Allows you to be certain that require and revert checks work as expected.
  • Ensures that your code works together and isn’t broken by other pieces of code.

Testing coverage

It’s a good idea to have a plan for how you will write your Solidity tests. This will help stop you from missing something obvious.

Your plan should include:

  1. A check that your functions return the proper output or state change.
  2. Consideration for known Solidity bugs and security threats.

To get started learning about known hacks, check out Solidity-by-Example.

Example Project

Recently, I built a multiple-signature wallet and tested it in Solidity.

You can see the code here.

Basically, the wallet can be set up with a single or group of admins who can add more admins and signers.

Admins can submit, sign, and execute transactions.

Signers can only submit and sign transactions.

I will walk through a few of the tests I wrote for the contract.

To see all the tests, check out the Github repository.

Hopefully, this will get you excited to write some tests in Solidity 😀

Setup

First, you’ll need to download and then initialize a new Dapptools project if you haven’t done it already.

To do, just follow the above links and then come back.

When you set up a new Dapptools project you will automatically have ds-test library installed and get a test contract labeled .t.sol.

The ds-test will give you logging and assertion functionality.

Notice that Dapptools automatically imports and setups inheritance for you.

In this file, you can write your test and run them by typing dapp test into your terminal.

I’ll cover some more testing commands a little later.

setup contracts for testing
setup contracts for testing

In our setup function, we’re deploying a new Multi-Sig Wallet with the parameters required by the constructor.

For quick reference, here’s what the Multi-Sig Wallet constructor looks like:

MultiSigWallet.sol constructor
MultiSigWallet.sol constructor

So, in the setup function, we’re setting the _admim parameter to the testing contract address.

The _signer is set to a different address we generate.

And _signaturesRequired for a transaction to be executed is set to 1.

The contracts named Alice and Bob are deployed to be used for testing the ownership functions.

Ownership

Let’s test adding an admin to the Contract.

Because the test contract is set as admin, it should have the power to add any other address as another admin to the contract.

But what if Alice tries to add herself as an admin to the contract?

Well, it should revert because Alice is not an admin unless the testing contract address adds her as one.

Alice tries to add herself as an Admin
Alice tries to add herself as an Admin

If we run dapp debug in our terminal we can select which test to debug and Dapptools will give us the ability to step through each line of our code.

This is an amazing feature.

There’s a lot here I won’t go into right now, but you can get a trace of your code and see what is happening at each line.

Here’s what it looks like when we debug the test_addAdmin function:

Alice fails to add herself as an Admin
Alice fails to add herself as an Admin

You’ll note that Alice failed to add herself as an admin to the contract.

This is what we wanted because she doesn’t have the necessary permissions (she’s not an admin!)

Submit Transaction

Next, let’s check that an admin can submit a transaction to the wallet.

Allow an admin to submit a transaction
Allow an admin to submit a transaction

We want both signers and admins to be able to submit transactions.

Unit vs Fuzz Testing

Now, let’s check that signers can submit a transaction.

Test that a signer address can submit a transaction to the wallet
Test that a signer address can submit a transaction to the wallet

Note that this test function has parameters.

The previous tests didn’t have function parameters and are called unit tests.

When you add parameters to your test functions they become property-based tests, also called fuzz tests.

With fuzz tests, Dapptools automatically attempt to use 100 different parameter variations.

This happens in seconds.

If you want to do more variations, let’s say 1000, you can do so easily by running dapp test --fuzz-runs 1000.

It’s that easy to check thousands of different possibilities.

This is important because an end-user could use a wide variety of different numbers as a function parameter.

We can now be more certain that the code is working as we expected.

Execute Transactions

Let’s make sure that signers cannot execute a transaction.

Remember, we want signers to be able to submit and sign transactions, but not execute them. That’s only for admins.

Test that signers cannot execute a transaction
Test that signers cannot execute a transaction

The cool thing about using dapp debug command along with try and catch is it gives you an awesome trace so you can see exactly where your code is reverting.

This allows you to visually confirm that your code is behaving as expected.

Finally, let’s test the whole admin process

Step thru the admin process
Step thru the admin process

Conclusion

That’s how to get started testing your smart contracts in Solidity.

I only covered the very basics, the rest is up to you, but I’m willing to bet that once you start testing in Solidity, you won’t go back.

At least that’s what happened to me 😀

Check out the references below if you want more material on Dapptools and testing.

If you have any questions, feel free to reach out to me at any time on Twitter or Github.

To see the rest of the code, check out the MultiSigWallet repository, you can find it on Github here.

References

YouTube: Become a Dapptools Pilled Chad in 30 minutes or Your Money Back

Subscribe to tobias
Receive the latest updates directly to your inbox.
Verification
This entry has been permanently stored onchain and signed by its creator.