Solidity / Smart contract тест бичих
Smart contract тест бичих
Smart contract тест нь онцгой чухал — deploy хийсний дараа алдаа засах боломжгүй. Hardhat нь Mocha тест framework болон Chai assertion library-г ашиглана.
Тест файлын бүтэц
test/SimpleStorage.js файл үүсгэнэ:
javascript
const { expect } = require("chai");
const { ethers } = require("hardhat");
describe("SimpleStorage", function () {
// Тест бүрийн өмнө шинэ contract deploy хийнэ
let storage;
let owner;
let otherAccount;
beforeEach(async function () {
// Тест хаягуудыг авна
[owner, otherAccount] = await ethers.getSigners();
// Contract factory авна
const SimpleStorage = await ethers.getContractFactory("SimpleStorage");
// Deploy хийнэ
storage = await SimpleStorage.deploy();
await storage.waitForDeployment();
});
// Тест бүлэг
describe("Байрлуулалт", function () {
it("Эзэмшигчийг зөв тохируулах ёстой", async function () {
expect(await storage.owner()).to.equal(owner.address);
});
it("Анхны утга 0 байх ёстой", async function () {
expect(await storage.get()).to.equal(0);
});
});
describe("set функц", function () {
it("Утгыг зөв хадгалах ёстой", async function () {
await storage.set(42);
expect(await storage.get()).to.equal(42);
});
it("Утгыг давтан өөрчлөх боломжтой байх ёстой", async function () {
await storage.set(10);
await storage.set(20);
expect(await storage.get()).to.equal(20);
});
});
});
Тест ажиллуулах:
bash
npx hardhat test
код
SimpleStorage
Байрлуулалт
✓ Эзэмшигчийг зөв тохируулах ёстой
✓ Анхны утга 0 байх ёстой
set функц
✓ Утгыг зөв хадгалах ёстой
✓ Утгыг давтан өөрчлөх боломжтой байх ёстой
4 passing (1s)
describe ба it
javascript
describe("Бүлгийн нэр", function () {
// Дэд бүлэг
describe("Дэд бүлэг", function () {
it("Тест нэр", async function () {
// Тест код
});
});
});
describe— тестүүдийг бүлэглэнэit— нэг тест тодорхойлноbeforeEach— тест бүрийн өмнө ажиллах код
expect — assertion
javascript
// Тэнцүү байх
expect(value).to.equal(42);
// Үнэн байх
expect(isActive).to.be.true;
expect(isActive).to.be.false;
// Хаяг тэнцүү байх
expect(addr).to.equal(owner.address);
// BigInt харьцуулалт
expect(balance).to.be.greaterThan(0n);
expect(balance).to.be.lessThanOrEqual(1000n);
// ETH хэмжээ харьцуулалт
expect(balance).to.equal(ethers.parseEther("1.0"));
Event тест
Үйл явдлыг зөв emit хийж байгааг шалгах:
javascript
describe("ValueChanged үйл явдал", function () {
it("set дуудахад ValueChanged гарах ёстой", async function () {
// Эхний утга
await storage.set(10);
// emit хийгдэх үйл явдлыг шалгана
await expect(storage.set(20))
.to.emit(storage, "ValueChanged")
.withArgs(10, 20); // (oldValue, newValue)
});
});
Revert тест
Алдааны нөхцлийг шалгах:
solidity
// Contract-д нэмсэн функц
function setPositive(uint256 value) public {
require(value > 0, "Утга 0-ээс их байх ёстой");
storedValue = value;
}
javascript
describe("Алдааны нөхцлүүд", function () {
it("0 утга оруулахад revert хийх ёстой", async function () {
await expect(storage.setPositive(0)).to.be.revertedWith(
"Утга 0-ээс их байх ёстой",
);
});
it("Custom error шалгах", async function () {
await expect(storage.restrictedFunction())
.to.be.revertedWithCustomError(storage, "Unauthorized")
.withArgs(otherAccount.address);
});
});
ETH гүйлгээ тест
ETH илгээж авдаг contract-ийн тест:
solidity
// contracts/Bank.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract Bank {
mapping(address => uint256) public balances;
function deposit() public payable {
require(msg.value > 0, "ETH илгээх шаардлагатай");
balances[msg.sender] += msg.value;
}
function withdraw(uint256 amount) public {
require(balances[msg.sender] >= amount, "Үлдэгдэл хүрэлцэхгүй");
balances[msg.sender] -= amount;
(bool success,) = payable(msg.sender).call{value: amount}("");
require(success, "Илгээхэд алдаа");
}
}
javascript
// test/Bank.js
const { expect } = require("chai");
const { ethers } = require("hardhat");
describe("Bank", function () {
let bank;
let owner;
let user;
beforeEach(async function () {
[owner, user] = await ethers.getSigners();
const Bank = await ethers.getContractFactory("Bank");
bank = await Bank.deploy();
await bank.waitForDeployment();
});
describe("deposit", function () {
it("ETH хийж, баланс нэмэгдэх ёстой", async function () {
const depositAmount = ethers.parseEther("1.0");
await bank.connect(user).deposit({ value: depositAmount });
expect(await bank.balances(user.address)).to.equal(depositAmount);
});
it("ETH хийхгүй бол revert хийх ёстой", async function () {
await expect(bank.connect(user).deposit({ value: 0 })).to.be.revertedWith(
"ETH илгээх шаардлагатай",
);
});
it("Contract-ын баланс нэмэгдэх ёстой", async function () {
const depositAmount = ethers.parseEther("2.0");
const bankAddress = await bank.getAddress();
await bank.connect(user).deposit({ value: depositAmount });
expect(await ethers.provider.getBalance(bankAddress)).to.equal(
depositAmount,
);
});
});
describe("withdraw", function () {
beforeEach(async function () {
// Withdraw тест хийхийн өмнө эхлээд deposit хийнэ
await bank.connect(user).deposit({ value: ethers.parseEther("1.0") });
});
it("Зөв дүн татан авах ёстой", async function () {
const withdrawAmount = ethers.parseEther("0.5");
const userBalanceBefore = await ethers.provider.getBalance(user.address);
const tx = await bank.connect(user).withdraw(withdrawAmount);
const receipt = await tx.wait();
const gasUsed = receipt.gasUsed * receipt.gasPrice;
const userBalanceAfter = await ethers.provider.getBalance(user.address);
expect(userBalanceAfter).to.equal(
userBalanceBefore + withdrawAmount - gasUsed,
);
});
it("Үлдэгдэлгүй бол revert хийх ёстой", async function () {
const tooMuch = ethers.parseEther("999.0");
await expect(bank.connect(user).withdraw(tooMuch)).to.be.revertedWith(
"Үлдэгдэл хүрэлцэхгүй",
);
});
});
});
Цаг хугацааны тест
Contract-д block.timestamp ашигладаг бол Hardhat-ын цаг удирдах функцийг хэрэглэнэ:
javascript
const { time } = require("@nomicfoundation/hardhat-network-helpers");
it("Хугацаа дуусмагц татаж авах боломжтой байх ёстой", async function () {
const deadline = (await time.latest()) + 7 * 24 * 60 * 60; // 7 хоног
// ... contract deploy, deposit ...
// Цагийг 7 хоног шилжүүлнэ
await time.increaseTo(deadline + 1);
// Одоо татан авах боломжтой
await expect(contract.withdraw()).to.not.be.reverted;
});
Тестийн coverage
bash
npx hardhat coverage
Ямар мөрийг тест хамарсан, хамраагүйг харуулна. 100% coverage зорино.
Дараагийн хичээлд:
Sepolia testnet-д contract deploy хийж, Etherscan дээр verify хийнэ.