Skip to content

Commit 2aa8821

Browse files
committed
Add "Writing Solidity tests" guide
1 parent 52bfa4b commit 2aa8821

File tree

2 files changed

+183
-0
lines changed

2 files changed

+183
-0
lines changed

src/content/hardhat3-alpha/learn-more/_dirinfo.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,5 @@ order:
77
href: /alpha-limitations
88
- title: Configuring the compiler
99
href: /configuring-the-compiler
10+
- title: Writing Solidity tests
11+
href: /writing-solidity-tests
Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
1+
# Writing unit tests in Solidity
2+
3+
Hardhat supports writing tests in both TypeScript and Solidity. TypeScript is typically used for higher-level integration tests, whereas Solidity is better suited for unit tests. This guide explains how to add Solidity tests to a Hardhat project, run them, and configure how they are executed. It assumes familiarity with Solidity tests and isn’t meant to serve as an introduction to them.
4+
5+
## Writing Solidity tests
6+
7+
A Solidity file is considered a test file if it’s inside the `test/` directory, or if it’s inside the `contracts/` directory and ends with `.t.sol`.
8+
9+
Every contract in a test file with functions that start with `test` is treated as a test contract. When the tests are run, Hardhat deploys every test contract and calls each of its test functions.
10+
11+
For example, suppose you have a `contracts/CounterTest.t.sol` or `test/CounterTest.sol` file with the following contract:
12+
13+
```solidity
14+
contract CounterTest {
15+
function testInc() public {
16+
Counter counter = new Counter();
17+
counter.inc();
18+
require(counter.count() == 1, "count should be 1");
19+
}
20+
}
21+
```
22+
23+
In this case, the test runner deploys the `CounterTest` contract and calls its `testInc` function. If the function execution reverts, the test is considered failed.
24+
25+
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:
26+
27+
```solidity
28+
contract CounterTest {
29+
function testIncBy(uint by) public {
30+
Counter counter = new Counter();
31+
counter.incBy(by);
32+
require(counter.count() == by, "count should match the 'by' value");
33+
}
34+
}
35+
```
36+
37+
### Assertion libraries
38+
39+
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).
40+
41+
To use `forge-std` in a Hardhat project, first install it:
42+
43+
::::tabsgroup{options=npm,pnpm}
44+
45+
:::tab{value=npm}
46+
47+
```bash
48+
npm install --save-dev github:foundry-rs/forge-std#v1.9.7
49+
```
50+
51+
:::
52+
53+
:::tab{value=pnpm}
54+
55+
```bash
56+
pnpm install --save-dev github:foundry-rs/forge-std#v1.9.7
57+
```
58+
59+
:::
60+
61+
::::
62+
63+
You can then import the `Test` base contract and extend your test contract from it. This lets you use helper functions like `assertEq`, which shows the mismatched values when the assertion fails:
64+
65+
```solidity
66+
import { Test } from "forge-std/src/Test.sol";
67+
68+
contract CounterTest is Test {
69+
function testIncBy(uint by) public {
70+
Counter counter = new Counter();
71+
counter.incBy(by);
72+
assertEq(counter.count(), by, "count should match the 'by' value");
73+
}
74+
}
75+
```
76+
77+
### Setup functions
78+
79+
Both the unit and fuzz test examples shown above create an instance of the `Counter` contract. You can share setup logic across tests using the `setup` function, which is called before each test execution:
80+
81+
```solidity
82+
contract CounterTest {
83+
Counter counter;
84+
85+
function setup() public {
86+
counter = new Counter();
87+
}
88+
89+
function testInc() public {
90+
counter.inc();
91+
require(counter.count() == 1, "count should be 1");
92+
}
93+
94+
function testIncBy(uint by) public {
95+
counter.incBy(by);
96+
require(counter.count() == by, "count should match the 'by' value");
97+
}
98+
}
99+
```
100+
101+
## Running Solidity tests
102+
103+
To run all your Solidity tests, use the `test solidity` task:
104+
105+
::::tabsgroup{options=npm,pnpm}
106+
107+
:::tab{value=npm}
108+
109+
```bash
110+
npx hardhat test solidity
111+
```
112+
113+
:::
114+
115+
:::tab{value=pnpm}
116+
117+
```bash
118+
pnpm hardhat test solidity
119+
```
120+
121+
:::
122+
123+
::::
124+
125+
You can also pass one or more paths as arguments, in which case only those files are executed:
126+
127+
::::tabsgroup{options=npm,pnpm}
128+
129+
:::tab{value=npm}
130+
131+
```bash
132+
npx hardhat test solidity <test-file-1> <test-file-2> ...
133+
```
134+
135+
:::
136+
137+
:::tab{value=pnpm}
138+
139+
```bash
140+
npx hardhat test solidity <test-file-1> <test-file-2> ...
141+
```
142+
143+
:::
144+
145+
::::
146+
147+
## Configuring Solidity tests
148+
149+
You can configure how Solidity tests are executed in your Hardhat configuration.
150+
151+
### Configuring the tests location
152+
153+
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:
154+
155+
```typescript
156+
paths: {
157+
tests: {
158+
solidity: "./solidity-tests"
159+
}
160+
},
161+
```
162+
163+
### Configuring the tests execution
164+
165+
To configure how Solidity tests are executed, use the `solidityTest` property in the Hardhat configuration.
166+
167+
For example, the `ffi` cheatcode is disabled by default for security reasons, but you can enable it:
168+
169+
```typescript
170+
solidityTest: {
171+
ffi: true
172+
},
173+
```
174+
175+
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`:
176+
177+
```typescript
178+
solidityTest: {
179+
sender: "0x1234567890123456789012345678901234567890"
180+
},
181+
```

0 commit comments

Comments
 (0)