Recommended resources
The freely available resources this lesson will direct you to.
- Build and Deploy Smart Contracts using Hardhat | [See Description]www.youtube.com
- Hardhat Testing Tutorial | Solidity Smart Contract Testing Developer | Hardhat Testing Coursewww.youtube.com
- Hardhat Guide: Automated Smart Contract Testingwww.krayondigital.com
- Getting started with Hardhat | Ethereum development ...hardhat.org
Hello! Welcome back to the course.
In the last module, we successfully built, deployed, and verified our ERC-20 token on a public testnet. We manually confirmed it works by transferring tokens between wallets on Etherscan. While this is a great milestone, it's not a scalable or professional way to ensure code quality. In backend development, you wouldn't deploy a new service to production after just a few manual clicks; you'd have a suite of automated unit and integration tests. The same principle applies here, and it's even more critical because smart contracts are immutable once deployed.
This lesson marks the beginning of our module on Automated Testing. We'll shift from manual checks on a live network to writing repeatable, automated tests that run locally. Today, our focus is on setting up the entire testing workbench.
By the end of this lesson, you will be able to:
- Set up a new Hardhat project configured for automated testing.
- Understand the roles of the core testing tools: Hardhat, ethers.js, and the Chai assertion library.
- Recognize the standard project structure for testing.
- Create the basic skeleton of a test file, ready for writing tests.
1. The Professional Testing Toolkit
In the world of Ethereum development, a few tools have become the industry standard for testing smart contracts. Your backend experience will make these concepts feel very familiar.
- Hardhat: This is our main development environment and will also act as our test runner. It's analogous to using a framework like Node.js with a test runner like Jest or Mocha. Hardhat orchestrates the entire testing process, from compiling your contracts to running the tests against a simulated blockchain.
- Hardhat Network: A built-in, local Ethereum network designed for development. Think of it as a mock server or an in-memory database. It starts up instantly, is incredibly fast, and gives you a clean slate for every test run, ensuring your tests are isolated and predictable.
- Ethers.js: The library we use to interact with the Ethereum blockchain (in our case, the local Hardhat Network). It allows our test scripts (written in TypeScript/JavaScript) to deploy contracts, call their functions, and check their state. This is similar to how you might use a database client library (like
pgfor PostgreSQL) to interact with a database from your backend code. - Mocha: A popular JavaScript test framework that provides the structure for our tests. It gives us functions like
describe()to group related tests andit()to define individual test cases. - Chai: An assertion library that provides expressive functions to check if our test outcomes are what we expect. Using
expect(value).to.equal(anotherValue)is much more readable than writingif (value !== anotherValue) throw new Error(...).
Fortunately, you don't need to install and configure all of these individually. The hardhat-toolbox plugin, which we'll install next, bundles them all together for a seamless experience.
2. Initializing Your Testing Project
While you have an existing project from the last module, we will create a fresh Hardhat project for our testing suite. This ensures we start with the correct, clean configuration recommended for testing.
The official Hardhat documentation is the best reference for these initial steps. We'll walk through it together.
Getting started with Hardhat | Ethereum development ...
Let's start by following the official Hardhat 'Getting started' guide to create a new project.
First, create a new empty directory for this project (e.g., my-token-tests). Navigate into it with your terminal. Follow the instructions under the 'Installation' section to initialize an npm project (npm init -y) and install Hardhat (npm install --save-dev hardhat). Then, proceed to the 'Quick Start' section and run npx hardhat init.
When you run npx hardhat init, you'll be presented with a few options. Since you're comfortable with TypeScript, let's select that.
$ npx hardhat init
👷 Welcome to Hardhat vX.X.X 👷
? What do you want to do? …
Create a JavaScript project
❯ Create a TypeScript project
Create an empty hardhat.config.js
Quit
Follow the prompts, accepting the defaults. It will ask if you want to install @nomicfoundation/hardhat-toolbox. Say yes! This is the key package that installs Ethers, Chai, and other testing utilities for us.
After the installation is complete, your project is ready.
3. Exploring the Project Structure
Your new project directory now contains several folders and files. Let's focus on the ones relevant to testing. The following video gives a great overview of this structure.
Build and Deploy Smart Contracts using Hardhat | [See Description]
Patrick Collins from Chainlink provides a clear walkthrough of the standard Hardhat project structure. This will help you understand where everything goes.
Watch from 02:59 to 04:13. Pay attention to his explanation of the contracts, scripts, and especially the test folders.
Here's a breakdown:
contracts/: This is where your Solidity source code lives. The sample project creates aLock.sol. You can delete this file and copy yourMyToken.solfile from your previous project into this folder.test/: This is our main focus. It's where all our test files will go. Hardhat automatically looks for and runs files in this directory when you execute the test command. The sample project includes aLock.ts. We will replace this with our own test file.hardhat.config.ts: The central configuration file. This is where you define which Solidity compiler version to use, configure network connections (like we did for Sepolia), and manage plugins. Thehardhat-toolboxis already imported and configured for you.package.json: Manages your project's dependencies, including Hardhat and the testing toolbox.
Now, run the default test command to see it in action:
npx hardhat test
You'll see Hardhat compile the Lock.sol contract and then run the tests from test/Lock.ts, printing a "passing" summary. This confirms that your testing environment is working correctly!

4. The Anatomy of a Test File
Now, let's set up the file where we will write the tests for our own MyToken contract.
- Delete the sample test file:
test/Lock.ts. - Create a new file:
test/MyToken.test.ts.
Inside this file, we will write the basic structure for our test suite. This involves importing the necessary tools and setting up the test blocks.
// Import utilities from Hardhat and Chai
import { expect } from "chai";
import { ethers } from "hardhat";
// `describe` is a container for a group of related tests.
// Here, we group all tests for the MyToken contract.
describe("MyToken", function () {
// This is a placeholder for our first test.
// `it` defines an individual test case. The string describes what the test should do.
it("Should correctly deploy and set the total supply", async function () {
// Test logic will go here in the next lesson.
});
// We can add more 'it' blocks for other tests.
it("Should have the correct name and symbol", async function () {
// Another test's logic will go here.
});
});
Let's break this down:
import { expect } from "chai";: This imports theexpectfunction from the Chai assertion library. We'll use this to make claims about our contract's state (e.g.,expect(await myToken.name()).to.equal("MyToken")).import { ethers } from "hardhat";: This imports the Ethers.js library, fully integrated with Hardhat. We'll use this to deploy our contract and interact with it.describe("MyToken", ...): This function from Mocha creates a test suite. All the tests related to theMyTokencontract will go inside this block. You can nestdescribeblocks to further organize your tests (e.g., a sub-group for transfer-related tests).it("Should...", async function () { ... }): This defines a single test case. The first argument is a human-readable string explaining what this specific test verifies. The function is markedasyncbecause nearly all interactions with a blockchain are asynchronous operations.
Test your understanding!
A colleague writes a test file but forgets the async keyword on their it block's function, like this: it("Should transfer tokens", function() { ... }). Inside the function, they use await to call a contract function. What will happen when they run the test?
Show answer
The test will fail with a syntax error: SyntaxError: await is only valid in async functions. JavaScript requires the async keyword on any function that uses await within it. Since all contract interactions via Ethers.js return Promises that need to be awaited, virtually all Hardhat test it blocks must be async.
Conclusion
Excellent work! You have successfully set up a professional, industry-standard environment for writing automated tests for your smart contracts. While we haven't written any actual test logic yet, preparing the workshop is a critical first step.
Key Takeaways:
- Automated testing is crucial for smart contract security and reliability.
- The
@nomicfoundation/hardhat-toolboxpackage provides an all-in-one solution, bundling Hardhat, the Hardhat Network, Ethers.js, Mocha, and Chai. - Hardhat projects follow a standard structure, with contracts in
contracts/and tests intest/. - Tests are organized into
describeblocks (test suites) anditblocks (individual test cases). - Test functions must be
asyncto handle the asynchronous nature of blockchain interactions.
Preview of the Next Lesson:
With our environment fully configured, we're ready to get to work. In the next lesson, we will populate our MyToken.test.ts file and write our first unit tests. We'll start by testing the most fundamental functions of our ERC-20 token: transfer() and balanceOf().