#Writing integration tests for plugins
While you can test some parts of your plugin in isolation, most testing happens through integration tests that verify how your plugin works with Hardhat and other plugins.
In Hardhat 3 there are two main approaches for writing integration tests.
#Testing with fixture projects
The simplest way to create an integration test for your plugin is by defining a Hardhat project and using it as a test fixture.
Here's how to do this using the Hardhat 3 plugin template. Follow this guide to learn how to use the template before continuing.
To define a fixture project in your template-based repository, create a directory in packages/plugin/test/fixture-projects
with a hardhat.config.ts
and a package.json
.
The package.json
can be fairly simple and should look something like this:
{
"name": "base-project",
"version": "1.0.0",
"type": "module",
"private": true
}
The hardhat.config.ts
can define whichever config you want, but it must import your plugin. Here's an example from packages/plugin/test/fixture-projects/base-project/hardhat.config.ts
:
import { HardhatUserConfig } from "hardhat/config";
import MyPlugin from "../../../src/index.js";
const config: HardhatUserConfig = {
plugins: [MyPlugin],
};
export default config;
Finally, your tests would look something like this:
import { createFixtureProjectHRE } from "./helpers/fixture-projects.js";
describe("Fixture test example", () => {
it("do something", async () => {
const hre = await createFixtureProjectHRE("base-project");
// Assert the behavior of your plugin
});
});
The createFixtureProjectHRE
function initializes a new Hardhat Runtime Environment based on a directory in packages/plugin/test/fixture-projects
. It uses that directory's hardhat.config.ts
and treats the directory as the project root.
#Testing with inline HardhatUserConfig
s
While fixture tests are simple, they can become cumbersome to maintain since your test and its data are spread across multiple files.
Alternatively, you can create a Hardhat Runtime Environment with an inline HardhatUserConfig
.
This type of test looks something like this:
import { createHardhatRuntimeEnvironment } from "hardhat/hre";
import MyPlugin from "../src/index.js";
describe("Inline config test example", () => {
it("do something", async () => {
const hre = await createHardhatRuntimeEnvironment({
plugins: [MyPlugin],
});
// Assert the behavior of your plugin
});
});
The hre
created with an inline config has two small differences:
- It won't have an
hre.config.paths.config
value. This field normally has the absolute path of the config file, but you are using an inline config. - Its
hre.config.paths.root
will be the closest directory to your current working directory that has apackage.json
file (just like npm does)
Both of these are customizable. Learn how by looking at the createFixtureProjectHRE
function in the Hardhat 3 plugin template.
#Using plugins as mocks
Another pattern that can be helpful for integration tests is defining another plugin exclusively for testing purposes.
This lets you customize Hardhat's behavior by mocking specific parts.
To do this, define a plugin and add it to your test HardhatUserConfig
's plugins
array before your actual plugin.
For example:
import { createHardhatRuntimeEnvironment } from "hardhat/hre";
import MyPlugin from "../src/index.js";
import MockNetworkPlugin from "./mocks/network-mocks.js";
describe("Mock plugin test example", () => {
it("do something", async () => {
const hre = await createHardhatRuntimeEnvironment({
plugins: [MockNetworkPlugin, MyPlugin],
});
// Setup the mock behavior
// Assert the behavior of your plugin
});
});
Your mock plugin can maintain state and export functions that control its behavior. For example, you could create functions to simulate network responses, configure fake data, or trigger specific conditions.
This works both with inline config and fixture projects.