Smart Contract Testing (Hardhat)
Hardhat adalah development environment untuk Ethereum. Fitur utamanya: local blockchain node, testing framework, dan debugging tools. Karena smart contract immutable setelah deploy, testing sangat krusial.
Setup Project
// npm install --save-dev hardhat @nomicfoundation/hardhat-toolbox
// npx hardhat init
// Pilih "Create a TypeScript project"
// Struktur project:
// contracts/ → Solidity files
// test/ → Test files
// scripts/ → Deploy scripts
// hardhat.config.ts
Test Dasar
import { expect } from "chai";
import { ethers } from "hardhat";
import { loadFixture } from "@nomicfoundation/hardhat-network-helpers";
describe("SimpleStorage", function () {
// Fixture — setup yang di-snapshot dan di-revert per test
async function deployFixture() {
const [owner, user1, user2] = await ethers.getSigners();
const SimpleStorage = await ethers.getContractFactory("SimpleStorage");
const storage = await SimpleStorage.deploy();
return { storage, owner, user1, user2 };
}
it("should store a value", async function () {
const { storage } = await loadFixture(deployFixture);
await storage.setValue(42);
expect(await storage.getValue()).to.equal(42);
});
it("should emit ValueChanged event", async function () {
const { storage, owner } = await loadFixture(deployFixture);
await expect(storage.setValue(100))
.to.emit(storage, "ValueChanged")
.withArgs(100, owner.address);
});
});
Testing Revert & Require
it("should revert if not owner", async function () {
const { storage, user1 } = await loadFixture(deployFixture);
await expect(storage.connect(user1).withdraw())
.to.be.revertedWith("Not owner");
});
it("should revert with custom error", async function () {
await expect(contract.doSomething())
.to.be.revertedWithCustomError(contract, "InsufficientBalance")
.withArgs(100, 50); // expected, actual
});
Testing ETH Transfers
it("should accept ETH deposits", async function () {
const { contract, user1 } = await loadFixture(deployFixture);
await expect(
contract.connect(user1).deposit({ value: ethers.parseEther("1.0") })
).to.changeEtherBalance(user1, ethers.parseEther("-1.0"));
expect(await ethers.provider.getBalance(contract.target))
.to.equal(ethers.parseEther("1.0"));
});
Time Manipulation
import { time } from "@nomicfoundation/hardhat-network-helpers";
it("should unlock after 7 days", async function () {
const { lock } = await loadFixture(deployFixture);
// Fast-forward 7 hari
await time.increase(7 * 24 * 60 * 60);
// Sekarang withdraw harus berhasil
await expect(lock.withdraw()).to.not.be.reverted;
});
Hardhat Local Node
# Jalankan local Ethereum node
npx hardhat node
# → Started HTTP and WebSocket JSON-RPC server at http://127.0.0.1:8545/
# → 20 test accounts, masing-masing 10000 ETH
# Deploy ke local node
npx hardhat run scripts/deploy.ts --network localhost
# Jalankan tests
npx hardhat test
npx hardhat test --grep "should store" # filter
Test Patterns
- Gunakan
loadFixtureuntuk snapshot/revert (cepat) - Test semua revert conditions (require, custom errors)
- Test event emissions
- Test edge cases: zero address, max uint, overflow
- Test access control (onlyOwner, roles)