Process
How to Improve Testing Productivity
Last updated:
December 13, 2021

Smart contract tests are slow. From compilation to running on a stack-based virtual machine, to running mainnet forks which require a significant number of HTTP requests – each step is a lot slower than its counterpart in traditional web/systems programming. This carries significant operational risk. Write too few tests and you risk missing security vulnerabilities. Write too many and you risk slowing down the development process to a halt. The only win-win solution is to speed up the tests themselves.

Below are a set of techniques that can help in improving testing productivity. We focus initially on speed optimizations and then on improving developer productivity when writing tests.

Cache compilation

Make sure you are using a development framework that caches compilation steps. EVM compilers can be very slow especially on a large number of contracts.

Use snapshots or persistent chains for unit testing

If some of your unit tests require a fixed configuration deployment of the same contracts, make sure to use snapshots or run tests sequentially on the same local instance (where possible).

Use caching for fork tests

Fork tests are some of the slowest kinds of tests. There are 2 main optimizations that can be used to speed up fork tests. The first is to make sure the block is pinned and requests are cached. Hardhat supports this feature out of the box, see Pinning a Block.

The second optimization is to run your own archive node.

Run fork tests on your own archive node

If you are willing to go the extra mile for developer productivity, you may want to consider deploying your own archive node locally for testing if you have a computer that meets the requirements. Erigon is currently the most popular archive node used in development given its low storage footprint.

Use clear & consistent code style

Lastly, the goal of optimizing test speed is ultimately to improve testing productivity. Another powerful lever is to make sure tests are using simple primitives and applying them consistently. This will make it easier for other members of the team to create more variations of existing tests.

Don't neglect debugging

When tests go wrong, make sure there is an easy way to figure out what happened. There are a couple of techniques that can be helpful here:

  • Print statements (e.g., using console.log in Hardhat)
  • Stack traces
  • Full-fledged visual debugging (for example enabled by Auditless Debugger).

See Also: