Smart Contract Testing (Hardhat) — Web3

Smart Contract Testing (Hardhat) Hardhat adalah development environment untuk Ethereum. Fitur utamanya: local blockchain node, testing framework, dan…

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

Yang akan kamu pelajari