Hardhat testing setup in Blockchain / Solidity - Time & Space Complexity
When running tests with Hardhat, it's important to understand how the time taken grows as the number of tests or contracts increases.
We want to know how the testing time changes when we add more test cases or contracts.
Analyze the time complexity of the following Hardhat test setup code.
const { expect } = require("chai");
describe("Token contract", function () {
let Token, token, owner, addr1;
beforeEach(async function () {
Token = await ethers.getContractFactory("Token");
token = await Token.deploy();
[owner, addr1] = await ethers.getSigners();
});
it("Should assign total supply to owner", async function () {
const ownerBalance = await token.balanceOf(owner.address);
expect(await token.totalSupply()).to.equal(ownerBalance);
});
});
This code sets up a test for a token contract, deploying it before each test and checking balances.
Identify the loops, recursion, array traversals that repeat.
- Primary operation: The
beforeEachhook runs before every test, redeploying the contract. - How many times: Once per test case inside the
describeblock.
As the number of test cases grows, the setup runs before each one, increasing total time.
| Input Size (number of tests) | Approx. Operations (deployments) |
|---|---|
| 10 | 10 deployments |
| 100 | 100 deployments |
| 1000 | 1000 deployments |
Pattern observation: The total setup time grows directly with the number of tests.
Time Complexity: O(n)
This means the total testing time grows in a straight line as you add more tests.
[X] Wrong: "Running more tests won't affect setup time much because deployment is fast."
[OK] Correct: Each test runs the setup separately, so deployment time adds up and grows with the number of tests.
Understanding how test setup time grows helps you write efficient tests and shows you can think about performance in real projects.
What if we moved the contract deployment from beforeEach to before? How would the time complexity change?
Practice
Solution
Step 1: Understand Hardhat testing role
Hardhat testing setup is designed to help developers write and run tests for smart contracts safely.Step 2: Identify the environment used
Tests run on a local blockchain, which means no real money or main network is involved.Final Answer:
To write and run tests for smart contracts on a local blockchain -> Option CQuick Check:
Hardhat testing = local smart contract tests [OK]
- Confusing testing with deployment to mainnet
- Thinking Hardhat mines real coins
- Assuming Hardhat builds user interfaces
Solution
Step 1: Recall Mocha test structure
Hardhat uses Mocha for testing, which organizes tests withdescribeblocks.Step 2: Identify correct syntax
Thedescribefunction takes a string and a callback function to group tests.Final Answer:
describe('MyContract', function() { ... }) -> Option BQuick Check:
Mocha test suites use describe() [OK]
- Using test() instead of describe() for suites
- Confusing suite() or context() with Mocha syntax
- Missing function keyword or arrow function
describe('SimpleTest', function() {
it('should return true', async function() {
const result = true;
console.log(result);
expect(result).to.equal(true);
});
});Solution
Step 1: Analyze console output
The code logs the variableresultwhich is set totrue, so console shows 'true'.Step 2: Check assertion
The test assertsresultequalstrue, which is correct, so test passes.Final Answer:
true and test passes -> Option AQuick Check:
console.log(true) + expect(true) passes [OK]
- Assuming expect is undefined without import
- Confusing console output with test result
- Thinking test fails because of async function
describe('Token', function() {
it('should deploy contract', async () => {
this.timeout(20000);
const Token = await ethers.getContractFactory('Token');
const token = await Token.deploy();
await token.waitForDeployment();
expect(token.address).to.be.a('string');
});
});Solution
Step 1: Check function style in it()
Using arrow functions in Mocha'sitblocks can cause loss ofthiscontext, which Hardhat sometimes relies on.Step 2: Verify other parts
Imports and await usage are correct; assertion syntax is valid for checking string type.Final Answer:
Using arrow function in it() causes context loss -> Option DQuick Check:
Use function() not arrow in it() [OK]
- Ignoring context loss with arrow functions
- Thinking deploy() is synchronous
- Assuming ethers import is automatic
Solution
Step 1: Understand event testing syntax
Hardhat with Chai usesawait expect(transaction).to.emit(contract, eventName).withArgs(...)to test events.Step 2: Check options for correct usage
await expect(token.transfer(addr1.address, 100)).to.emit(token, 'Transfer').withArgs(owner.address, addr1.address, 100); correctly awaits the transaction and specifies contract and event with arguments.Step 3: Identify errors in other options
The other options either miss theawaitbeforeexpect, fail to specify the contract instance into.emit(), or use incorrect syntax chaining.Final Answer:
await expect(token.transfer(addr1.address, 100)).to.emit(token, 'Transfer').withArgs(owner.address, addr1.address, 100); -> Option AQuick Check:
Use await expect(...).to.emit(contract, event) [OK]
- Forgetting to await the expect call
- Not specifying the contract in to.emit()
- Calling expect on transaction result instead of promise
Ready to prove it?
Pass the test and this pattern moves unknown
