How to Deploy Smart Contract Once per Test
Here we’ll extract the fork-deploy code to a separate function for reusability to forkAndDeployGreeter.
This is pretty self-explanatory - we create a Fork and deploy smart contracts to it in each individual test, before issuing transactions. You can think of it as a Fork dedicated to one and only one test.
This way each test function gets a clean contract with an unmodified state - the one present after initialization. Here we have level isolation of smart contracts under test. It’s pretty much how you’d want to test a class/service - having it cleanly initialized for each test you have.
Note that deployment time may significantly increase depending on the number of smart contracts you have.
It’s advisable to remove the Fork upon successful completion of the test by executing fork.removeFork() at the very end of each test (it or afterEach). The first approach keeps the Fork in case of test failure, which is handy for examination. The latter will remove the Fork even if your test fails.
These tests provide the highest level of isolation, and transactions remain persisted. Individual tests have corresponding Forks visible in Tenderly Dashboard.
Speed of test suites using this approach may become an issue, as the time it takes to run such a suite is
$O(N_TN_{SC})$
. You can find one proposed solution here.
1
import { fail } from "assert";
2
import { expect } from "chai";
3
import { ethers } from "hardhat";
4
import { forkForTest } from "./fork";
5
6
/** a reusable function to deploy the contract under test to the newly created fork */
7
const forkAndDeployGreeter = async () => {
8
const fork = await forkForTest({ network_id: "1", block_number: 14386016 });
9
// deploy the contract
10
11
const Greeter = await ethers.getContractFactory(
12
"Greeter",
13
fork.provider.getSigner()
14
);
15
const greeter = await Greeter.deploy("Hello, world!");
16
17
await greeter.deployed();
18
return { fork, greeter }
19
}
20
21
describe("Greeter", function () {
22
it("Should return the new greeting once it's changed", async function () {
23
// fork and deploy greeter to the fork
24
const { fork, greeter } = await forkAndDeployGreeter();
25
expect(await greeter.greet()).to.be.equal("Hello, world!");
26
27
await (await greeter.setGreeting("Bonjour le monde!")).wait();
28
expect(await greeter.greet()).to.be.equal("Bonjour le monde!");
29
30
// remove the fork each time test succeeds
31
fork.removeFork();
32
});
33
34
it("Should also return the new greeting once it's changed", async function () {
35
const { fork, greeter } = await forkAndDeployGreeter();
36
const secondGreeter = fork.signers[1];
37
38
await (
39
await greeter
40
.connect(secondGreeter)
41
.setGreeting("Hola, mundo!")
42
).wait();
43
44
expect(await greeter.greet()).to.equal("Hola, mundo!");
45
await fork.removeFork();
46
});
47
48
});
Copied!