Menulis pengujian kontrak pintar: test berbasis Solidity dengan forge test dan cheatcode vm.prank, vm.deal, serta vm.warp di Foundry, test berbasis Mocha/Chai + ethers.js di Hardhat, dan pengukuran coverage dengan solidity-coverage

Setelah di episode 9 kalian mengenal Foundry dan Hardhat sebagai toolchain, pada episode ini kita membahas bagian yang paling menentukan kualitas produksi: testing.
Mengapa testing begitu penting di Solidity? Karena kontrak yang di-deploy adalah immutable — bug yang lolos ke mainnet bisa berarti dana pengguna hilang permanen. Berbeda dengan aplikasi biasa yang bisa di-hotfix, kontrak bermasalah membutuhkan migrasi, proxy, atau kerja keras komunitas. Oleh karena itu, kebiasaan menulis test sebelum release bukan sekadar best practice — ini kebutuhan keselamatan.
Test Foundry ditulis dalam Solidity menggunakan library forge-std. Konvensinya: fungsi testXxx dianggap test, dan ada pula pola testFuzzXxx untuk fuzzing.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.36;
import {Test} from "forge-std/Test.sol";
import {HelloWorld} from "../src/HelloWorld.sol";
contract HelloWorldTest is Test {
HelloWorld hello;
function setUp() public {
hello = new HelloWorld("Awal");
}
function test_InitialGreeting() public view {
assertEq(hello.greeting(), "Awal");
}
function test_SetGreeting() public {
hello.setGreeting("Baru");
assertEq(hello.greeting(), "Baru");
}
}forge test -vvvsetUp() dijalankan sebelum setiap test, memastikan isolasi. Output -vvv menampilkan log dan stack trace detail saat test gagal.
Cheatcode adalah fungsi vm.* yang memanipulasi state EVM untuk simulasi yang sulit dilakukan manual:
| Cheatcode | Fungsi | Contoh penggunaan |
|---|---|---|
vm.prank(addr) | Set msg.sender untuk panggilan berikutnya | Menguji sebagai pemilik |
vm.startPrank(addr) | Sama, untuk banyak panggilan | Transaksi berurutan |
vm.deal(addr, amount) | Beri ETH ke alamat | Menguji fungsi payable |
vm.warp(ts) | Lompatkan block.timestamp | Menguji waktu/kadaluarsa |
vm.roll(n) | Set block.number | Menguji logika blok |
function test_OnlyOwner() public {
address alice = makeAddr("alice");
vm.deal(alice, 1 ether);
// Panggil deposit seolah-olah dari alice
vm.prank(alice);
vault.deposit{value: 1 ether}();
assertEq(vault.balances(alice), 1 ether);
}
function test_Expired_After_Warp() public {
vm.warp(block.timestamp + 365 days);
// logika yang bergantung waktu sekarang menganggap sudah lewat
}Cheatcode inilah yang membuat Foundry sangat produktif: menguji access control, timing, dan pengiriman ETH menjadi sepele.
Test Hardhat ditulis dalam JavaScript/TypeScript dengan Mocha + Chai dan ethers.js:
const { expect } = require("chai");
const { ethers } = require("hardhat");
describe("HelloWorld", function () {
it("harus mengembalikan greeting awal", async function () {
const HelloWorld = await ethers.getContractFactory("HelloWorld");
const hello = await HelloWorld.deploy("Awal");
await hello.waitForDeployment();
expect(await hello.greeting()).to.equal("Awal");
});
});npx hardhat testHardhat menyediakan helper loadFixture untuk mem-deploy sekali per describe block dan snapshot/revert otomatis per test — menjaga suite tetap cepat:
const { loadFixture } = require("@nomicfoundation/hardhat-toolbox/network-helpers");
async function deployFixture() {
const hello = await (await ethers.getContractFactory("HelloWorld")).deploy("Awal");
return { hello };
}
it("test 1", async () => { const { hello } = await loadFixture(deployFixture); ... });
it("test 2", async () => { const { hello } = await loadFixture(deployFixture); ... });Coverage mengukur berapa banyak baris kode yang dieksekusi oleh test:
npx hardhat coverageDi Foundry, kombinasi forge test --gas-report dan forge coverage (via plugin foundry-coverage) memberi gambaran serupa. Coverage yang tinggi bukan jaminan aman, tapi coverage rendah adalah tanda bahaya — target yang sehat adalah di atas 90% untuk fungsi kritis.
Warning
Ingat: coverage hanya mengukur baris yang tereksekusi, bukan kebenaran logika. Uji jalur negatif (fungsi yang harus revert, access control yang ditolak, transfer yang gagal) — bukan hanya jalur bahagia. Ini yang membedakan test yang baik dari test yang sekadar ada.
vm.prank/vm.deal/vm.warp untuk simulasi.loadFixture.Inti yang harus dibawa pulang:
forge test) dengan cheatcode vm.prank, vm.deal, vm.warp.loadFixture.Di episode 11 selanjutnya kita akan membahas token standards: ERC-20 & ERC-721 — fungsi transfer, approve, transferFrom, konsep decimals, membangun token dengan OpenZeppelin, NFT dengan metadata, royalty ERC-2981, dan multi-token ERC-1155. Sampai jumpa di episode 11!