npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@mangrovedao/hardhat-test-solidity

v0.0.17

Published

Hardhat plugin for writing tests in solidity

Downloads

6

Readme

hardhat-test-solidity

💡 Inspired by dapptools's ds-test.

⚠️ Requires hardhat-deploy as a peer dependency.

tl;dr

Tests suites are solidity contracts, tests are solidity functions.

Install

terminal

$ npm install @mangrovedao/hardhat-test-solidity

hardhat.config.js

require('@mangrovedao/hardhat-test-solidity');

MyContract_Test.sol:

// Adds Test library to the context
import Test from "@mangrovedao/hardhat-test-solidity/test.sol";

// `_Test` suffix means it is a test contract
contract MyContract_Test {

  receive() external payable {} // necessary to receive eth from test runner

  // `_test` suffix means it is a test function
  function addition_test() public {
    prepare();
    // Logging will be interpreted by hardhat-test-solidity
    Test.eq(4,2+2,"oh no");
  }

  // Will not be interpreted as a test function
  function prepare() public {}
}

terminal

$ npx hardhat test-solidity MyContract

How to structure test contract

  • All contracts with suffix _Test are test contracts.
  • All public functions with suffix _test are test functions.
  • All public functions with suffix _beforeAll setup the state before other tests.
  • If you have the functions fn_test and fn_before, fn_before will run, then fn_test, without state revert.

Tips

  • You must make a receive function so your test contracts get ethers.
  • State is reverted between tests
  • Functions are sorted alphabetically before they are added to test suite
  • You probably want a MyContract_Test for every MyContract.
  • Having multiple _beforeAll functions rather than setting things up in the constructor means you can split setup into multiple transactions to go around gas limits.

How to test for stuff

  • Test.check(bool success,string memory message) succeeds if success is true.
  • Test.eq(actual,expected,message) for testing bytes32, bool, string, uint, address equality.
  • Test.eq0(actual,expected,message) for testing bytes equality.
  • Test.less(uint a, uint b,message) succeeds if a < b.
  • Test.more(uint a, uint b,message) succeeds if a > b.
  • Test.fail(message) to always fail.
  • Test.succeed() to always succeed.

How to test for event emission

Suppose you want to make sure that contract Market emits the Trade event.

import "@mangrovedao/hardhat-test-solidity/test.sol";
contract My_Test {
  receive() external payable {} // necessary to receive eth from test runner

  Market amm;

  function _beforeAll() {
    amm = new Market()
  }

  function first_test() {
    Market.trade();

    Test.expectFrom(amm);
    emit Market.Trade();
  }
}

So: you emit an event (with any arguments you like), and the plugin will check that amm has already emitted the exact same event.

Tips

  • For a given address, the order of events matters. Between addresses it does not.
  • Events are checked after their emission. So do all your test, and at the end test for events.
  • If you want to normally emit events from a test after you already called Test.expectFrom(address), call Test.stopExpecting();

How to use the command line

npx hardhat test-solidity [contract names without _Test] [--prefix function_prefix]

Add --details for more detailed logging, including logs generated by the logFormatters plugin option (see below).

For more CLI options look at

npx hardhat test-solidity --help

Tip

If you want to only run the test breath_is_fire_test in the testing contract Dragon_Test, and you have 10 testing contracts, run

test-solidity Dragon --prefix breath_is_fire

rather than just

test-solidity --prefix breath_is_fire

so you don't waste time deploying all the other test suite contracts.

How to log

To get nicely-formatted logs, use the Display library. There are

  • Display.log(uint|string)
  • Display.log(uint|string,uint|string)
  • Display.log(uint|string,uint|string,uint|string)
  • Display.logBalances(address[1] memory tokens, address a0)
  • Display.logBalances(address[1] memory tokens, address a0, address a1)
  • Display.logBalances(address[1] memory tokens, address a0, address a1, address a2)
  • Display.logBalances(address[2] memory tokens, address a0)
  • Display.logBalances(address[2] memory tokens, address a0, address a1)
  • Display.logBalances(address[2] memory tokens, address a0, address a1, address a2)

How to register addresses

To get pretty-printing of addresses, in your test setup do

import {Display as D} from "@mangrovedao/hardhat-test-solidity/test.sol";
...
D.register(address addr, string memory name)

then when using --show-events and wherever addresses are used, name will be shown instead of addr.

How to configure the plugin

hardhat.config.js

{
  ...,
  testSolidity: { // default values as follows:
    timeout: 300_000 /* test suite timeout in ms */,
    logFormatters: (hre,formatArg) => { return {}; } /* format logs */
    testers: (hre,formatArg,assert) => { return {}; } /* format logs */
  }
}

Custom log formatters

logFormatters(hre,formatArg):object is a function that takes the hre hardhat runtime environment and a formatArg(arg,type?):string utility function. arg is dynamically tested and type is an optional type hint (it can be uint, address, or an array of type hints) to help formatting.

logFormatters should return an object where keys are event names and values are formatting functions that should directly log to console and have type:

(log:ethers.LogDescription,rawLog:{topics,data},originator:string):void

Tip

See src/logFormatters.js for examples.

Custom testers/assertions

testers(hre,formatArg,assert/*assert library*/) takes the hre, formatArg, and the chai assert object, and returns an object where keys are test event names and values are of the form :

{
  trigger({success,message,actual,expected}) : void
}

You should create the corresponding events in your tests so that they can be interpreted by your functions.

Tip

See src/testers.js for examples.

Debugging

This plugin uses the debug package. To debug this plugin only do:

DEBUG='hardhat:test-solidity:*' npx hardhat test-solidity [args]