# Writing unit tests in Solidity

Description: How to write unit tests in Solidity

Note: This document was authored using MDX

  Source: https://github.com/NomicFoundation/hardhat-website/tree/main/src/content/docs/docs/guides/testing/using-solidity.mdx

  Components used in this page:
    - <Run cmd="..."/>: Runs a command in the terminal with npm/pnpm/yarn.
    - <Install pkg="..."/>: Installs a package in the terminal with npm/pnpm/yarn.

import Install from "@hh/Install.astro";
import Run from "@hh/Run.astro";

Hardhat has built-in support for Solidity tests. You don't need to install any plugin to use them.

## Writing Solidity tests

A Solidity file is considered a **test file** if:

- It's inside the `test/` directory
- It's inside the `contracts/` directory and ends with `.t.sol`

Both of these directories can be changed in your Hardhat [configuration](/docs/reference/configuration), but these are the default ones.

If a contract in a test file has at least one function that starts with `test`, it's considered a **test contract**. When the tests are run, Hardhat deploys every test contract and calls each of its test functions.

For example, if you have a file named `contracts/CounterTest.t.sol` or `test/CounterTest.sol` with the following contract:

```solidity
contract CounterTest {
  function testInc() public {
    Counter counter = new Counter();
    counter.inc();
    require(counter.count() == 1, "count should be 1");
  }
}
```

the test runner will deploy the `CounterTest` contract and call its `testInc` function. If the function execution reverts, the test is considered failed.

### Fuzz tests

Hardhat also supports **fuzz tests**, which are similar to regular tests but accept parameters. When the tests are executed, fuzz test functions are called multiple times with random values as arguments:

```solidity
contract CounterTest {
  function testIncBy(uint by) public {
    Counter counter = new Counter();
    counter.incBy(by);
    require(counter.count() == by, "count should match the 'by' value");
  }
}
```

### Using assertion libraries

In the previous example, the error message doesn't show the actual value of `by` that made the test fail. That's because interpolating the value into the string isn't straightforward in Solidity. To get better error messages, plus other useful functionality, you can use an assertion library like [forge-std](https://github.com/foundry-rs/forge-std).

To use `forge-std` in a Hardhat project, first install it:

<Install packages="'github:foundry-rs/forge-std#v1.9.7'" />

You can then import the `Test` base contract and extend your test contracts from it. This lets you use helper functions like `assertEq`, which shows the mismatched values when the assertion fails:

```solidity
import { Test } from "forge-std/Test.sol";

contract CounterTest is Test {
  function testIncBy(uint by) public {
    Counter counter = new Counter();
    counter.incBy(by);
    assertEq(counter.count(), by, "count should match the 'by' value");
  }
}
```

### Setup functions

Both the unit and fuzz test examples shown above create an instance of the `Counter` contract. You can share setup logic like that across tests using the `setUp` function, which is called before each test execution:

```solidity
contract CounterTest {
  Counter counter;

  function setUp() public {
    counter = new Counter();
  }

  function testInc() public {
    counter.inc();
    require(counter.count() == 1, "count should be 1");
  }

  function testIncBy(uint by) public {
    counter.incBy(by);
    require(counter.count() == by, "count should match the 'by' value");
  }
}
```

### Using cheatcodes

When writing Solidity tests in Hardhat, you can use [Solidity test cheatcodes](/docs/reference/cheatcodes/cheatcodes-overview) to manipulate the EVM state and control the execution environment of your tests.

For example, you can use the [`vm.prank`](/docs/reference/cheatcodes/environment/prank) cheatcode to change the `msg.sender` for the next call:

```solidity
import { Test } from "forge-std/Test.sol";

contract CounterTest is Test {
  Counter counter;

  function setUp() public {
    counter = new Counter();
  }

  function testIncAsAlice() public {
    address alice = address(0x123);
    vm.prank(alice);
    counter.inc();
    assertEq(counter.count(), 1, "count should be 1");
    assertEq(counter.lastCaller(), alice, "last caller should be alice");
  }
}
```

## Running Solidity tests

You can run all the tests in your Hardhat project using the `test` task:

<Run command="hardhat test" />

If you only want to run your Solidity tests, use the `test solidity` task instead:

<Run command="hardhat test solidity" />

You can also pass one or more paths as arguments to these tasks, in which case only those files are executed:

<Run command="hardhat test <test-file-1> <test-file-2> ..." />

### Multichain support

By default, Solidity tests run in an environment that simulates Ethereum Mainnet. If you're building for other blockchains, you can test against their specific behavior by specifying a different Chain Type with the `--chain-type` option:

<Run command="hardhat test solidity --chain-type op" />

This example uses the `op` Chain Type, which simulates OP Mainnet and its testnets.

To learn more about Chain Types and testing on different blockchains, read our [multichain support](/docs/explanations/multichain-support) explanation.

## Configuring Solidity tests

You can configure how Solidity tests are executed in your Hardhat configuration.

### Configuring the tests location

By default, Hardhat treats every Solidity file in the `test/` directory as a test file. To use a different location, set the `paths.tests.solidity` field:

```ts
// hardhat.config.ts
import { defineConfig } from "hardhat/config";

export default defineConfig({
  /// ... other config ...
  paths: {
    tests: {
      solidity: "./solidity-tests",
    },
  },
});
```

### Configuring the tests execution

To configure how Solidity tests are executed, use the `test.solidity` object in the Hardhat configuration.

For example, the `ffi` cheatcode is disabled by default for security reasons, but you can enable it:

```ts
// hardhat.config.ts
import { defineConfig } from "hardhat/config";

export default defineConfig({
  /// ... other config ...
  test: {
    solidity: {
      ffi: true,
    },
  },
});
```

It's also possible to modify the execution environment of the tests. For example, you can modify the address that is returned by `msg.sender`:

```ts
// hardhat.config.ts
import { defineConfig } from "hardhat/config";

export default defineConfig({
  /// ... other config ...
  test: {
    solidity: {
      from: "0x1234567890123456789012345678901234567890",
    },
  },
});
```

To learn more about how to configure your Solidity tests, read [the Solidity tests Configuration reference](/docs/reference/configuration#solidity-tests-configuration).

You can also override config settings for individual test functions using inline configuration. Read the [Inline configuration](/docs/guides/testing/inline-configuration) guide to learn more.
