智能合約測試框架全解析

概述

智能合約測試庫是區塊鏈開發中至關重要的工具,用于確保智能合約的安全性、正確性和可靠性。以下是主流的智能合約測試庫及其詳細解析。

一、主流測試框架對比

測試框架開發語言主要特點適用場景
Hardhat + WaffleJavaScript/TypeScript強大的調試功能,豐富的插件生態復雜的DeFi項目,需要詳細調試的場景
TruffleJavaScript完整的開發套件,內置測試框架初學者,快速原型開發
Foundry (Forge)Solidity極快的測試速度,原生Solidity測試追求測試速度,熟悉Solidity的團隊
BrowniePythonPython語法,豐富的插件系統Python開發者,快速開發

二、Hardhat + Waffle 詳細解析

1. 安裝和配置

# 初始化項目
npm init -y# 安裝Hardhat
npm install --save-dev hardhat# 初始化Hardhat項目
npx hardhat# 安裝Waffle和相關依賴
npm install --save-dev @nomiclabs/hardhat-waffle ethereum-waffle chai @nomiclabs/hardhat-ethers ethers

2. 配置文件示例

// hardhat.config.js
require("@nomiclabs/hardhat-waffle");
require("@nomiclabs/hardhat-ethers");
require("solidity-coverage"); // 測試覆蓋率工具module.exports = {solidity: {version: "0.8.19",settings: {optimizer: {enabled: true,runs: 200}}},networks: {hardhat: {chainId: 1337,// 用于測試的初始賬戶配置accounts: {mnemonic: "test test test test test test test test test test test junk",count: 20}},localhost: {url: "http://127.0.0.1:8545"}},mocha: {timeout: 40000 // 測試超時時間}
};

3. 測試結構詳解

// 引入必要的庫和工具
const { expect } = require("chai");
const { ethers, waffle } = require("hardhat");
const { loadFixture, deployContract } = waffle;// 模擬提供者,用于測試中模擬區塊鏈狀態
const { provider } = waffle;// 描述測試套件
describe("MyContract Test Suite", function () {// 聲明變量let owner, user1, user2;let myContract;let token;// 裝置函數 - 用于設置測試環境async function deployContractsFixture() {// 獲取簽名者[owner, user1, user2] = await ethers.getSigners();// 部署合約const MyContract = await ethers.getContractFactory("MyContract");myContract = await MyContract.deploy();await myContract.deployed();// 部署ERC20代幣用于測試const Token = await ethers.getContractFactory("ERC20Mock");token = await Token.deploy("Test Token", "TT", owner.address, ethers.utils.parseEther("1000"));await token.deployed();return { myContract, token, owner, user1, user2 };}// 在每個測試用例前執行beforeEach(async function () {// 加載裝置,確保每個測試有干凈的環境({ myContract, token, owner, user1, user2 } = await loadFixture(deployContractsFixture));});// 測試用例組:基本功能describe("Basic Functionality", function () {it("Should deploy with correct initial values", async function () {// 驗證初始狀態expect(await myContract.owner()).to.equal(owner.address);expect(await myContract.isActive()).to.be.true;});it("Should revert when unauthorized user calls admin function", async function () {// 測試權限控制await expect(myContract.connect(user1).adminFunction()).to.be.revertedWith("Unauthorized");});});// 測試用例組:事件測試describe("Events", function () {it("Should emit ValueChanged event when value is updated", async function () {const newValue = 42;// 測試事件發射await expect(myContract.setValue(newValue)).to.emit(myContract, "ValueChanged").withArgs(owner.address, newValue);});});// 測試用例組:資金相關測試describe("ETH Transactions", function () {it("Should handle ETH transfers correctly", async function () {const depositAmount = ethers.utils.parseEther("1.0");// 測試ETH轉賬和余額變化await expect(() =>myContract.connect(user1).deposit({ value: depositAmount })).to.changeEtherBalance(user1, depositAmount.mul(-1));expect(await myContract.getBalance(user1.address)).to.equal(depositAmount);});it("Should revert when insufficient ETH is sent", async function () {const insufficientAmount = ethers.utils.parseEther("0.5");await expect(myContract.connect(user1).deposit({ value: insufficientAmount })).to.be.revertedWith("Insufficient ETH");});});// 測試用例組:ERC20代幣交互describe("ERC20 Interactions", function () {it("Should handle token transfers correctly", async function () {const transferAmount = ethers.utils.parseEther("100");// 授權合約可以操作代幣await token.connect(user1).approve(myContract.address, transferAmount);// 測試代幣轉賬await expect(() =>myContract.connect(user1).depositTokens(token.address, transferAmount)).to.changeTokenBalance(token, user1, transferAmount.mul(-1));});});// 測試用例組:邊界情況測試describe("Edge Cases", function () {it("Should handle maximum values correctly", async function () {const maxUint256 = ethers.constants.MaxUint256;// 測試邊界值await expect(myContract.setValue(maxUint256)).to.emit(myContract, "ValueChanged").withArgs(owner.address, maxUint256);});it("Should handle zero values correctly", async function () {// 測試零值處理await expect(myContract.setValue(0)).to.emit(myContract, "ValueChanged").withArgs(owner.address, 0);});});// 測試用例組:重入攻擊防護測試describe("Reentrancy Protection", function () {it("Should prevent reentrancy attacks", async function () {// 部署惡意合約測試重入攻擊const MaliciousContract = await ethers.getContractFactory("MaliciousContract");const maliciousContract = await MaliciousContract.deploy(myContract.address);await maliciousContract.deployed();// 存款const depositAmount = ethers.utils.parseEther("1.0");await maliciousContract.deposit({ value: depositAmount });// 嘗試重入攻擊await expect(maliciousContract.attack()).to.be.reverted;});});// 測試用例組:Gas消耗測試describe("Gas Optimization", function () {it("Should have reasonable gas costs for common operations", async function () {const tx = await myContract.setValue(42);const receipt = await tx.wait();// 檢查Gas消耗expect(receipt.gasUsed).to.be.lt(100000); // 確保Gas消耗在合理范圍內});});
});

三、高級測試技巧

1. 時間相關的測試

describe("Time-based Functions", function () {it("Should allow withdrawal only after lock period", async function () {const { myContract, user1 } = await loadFixture(deployContractsFixture);const depositAmount = ethers.utils.parseEther("1.0");// 存款await myContract.connect(user1).deposit({ value: depositAmount });// 嘗試提前取款(應該失敗)await expect(myContract.connect(user1).withdraw()).to.be.revertedWith("Lock period not ended");// 時間旅行:快進到鎖定期結束后const lockPeriod = await myContract.lockPeriod();await network.provider.send("evm_increaseTime", [lockPeriod.toNumber() + 1]);await network.provider.send("evm_mine");// 現在應該可以成功取款await expect(myContract.connect(user1).withdraw()).to.not.be.reverted;});
});

2. 復雜狀態測試

describe("Complex State Tests", function () {it("Should handle multiple interactions correctly", async function () {const { myContract, user1, user2 } = await loadFixture(deployContractsFixture);// 模擬多個用戶交互const actions = [];for (let i = 0; i < 10; i++) {if (i % 2 === 0) {actions.push(myContract.connect(user1).setValue(i));} else {actions.push(myContract.connect(user2).setValue(i));}}// 執行所有操作await Promise.all(actions);// 驗證最終狀態const finalValue = await myContract.getValue();expect(finalValue).to.equal(9); // 最后一個設置的值});
});

3. 模擬和存根

describe("Mocking and Stubbing", function () {it("Should work with mock dependencies", async function () {// 部署模擬合約const MockERC20 = await ethers.getContractFactory("MockERC20");const mockToken = await MockERC20.deploy();await mockToken.deployed();// 設置模擬行為await mockToken.setMockBalance(user1.address, ethers.utils.parseEther("1000"));await mockToken.setMockAllowance(user1.address, myContract.address, ethers.utils.parseEther("1000"));// 測試與模擬合約的交互const transferAmount = ethers.utils.parseEther("100");await expect(() =>myContract.connect(user1).depositTokens(mockToken.address, transferAmount)).to.changeTokenBalance(mockToken, user1, transferAmount.mul(-1));});
});

四、測試最佳實踐

1. 測試組織結構

tests/
├── unit/                 # 單元測試
│   ├── MyContract.test.js
│   └── utils.test.js
├── integration/          # 集成測試
│   ├── Interactions.test.js
│   └── CrossContract.test.js
├── security/             # 安全測試
│   ├── Reentrancy.test.js
│   └── AccessControl.test.js
└── gas/                  # Gas優化測試└── GasProfiling.test.js

2. 測試覆蓋率

# 安裝測試覆蓋率工具
npm install --save-dev solidity-coverage# 運行測試并生成覆蓋率報告
npx hardhat coverage# 或者在hardhat.config.js中配置
module.exports = {// ... 其他配置coverage: {url: 'http://127.0.0.1:8555' // 覆蓋率專用的本地網絡}
};

3. 持續集成配置

# .github/workflows/test.yml
name: Smart Contract Testson: [push, pull_request]jobs:test:runs-on: ubuntu-lateststeps:- uses: actions/checkout@v2- name: Setup Node.jsuses: actions/setup-node@v2with:node-version: '16'- name: Install dependenciesrun: npm ci- name: Run testsrun: npx hardhat test- name: Generate coverage reportrun: npx hardhat coverage- name: Upload coverage to Codecovuses: codecov/codecov-action@v2with:file: ./coverage.json

五、常見測試模式

1. 權限測試模式

// 測試只有所有者可以調用的函數
async function testOnlyOwner(functionCall, ...args) {const [owner, nonOwner] = await ethers.getSigners();const contract = await deployContract();// 非所有者調用應該失敗await expect(contract.connect(nonOwner)[functionCall](...args)).to.be.revertedWith("Ownable: caller is not the owner");// 所有者調用應該成功await expect(contract.connect(owner)[functionCall](...args)).to.not.be.reverted;
}

2. 狀態機測試模式

// 測試合約狀態轉換
describe("State Machine Tests", function () {const States = {OPEN: 0,CLOSED: 1,FINALIZED: 2};it("Should follow correct state transitions", async function () {const contract = await deployContract();// 初始狀態expect(await contract.state()).to.equal(States.OPEN);// 轉換到關閉狀態await contract.close();expect(await contract.state()).to.equal(States.CLOSED);// 嘗試非法狀態轉換await expect(contract.open()).to.be.revertedWith("Invalid state transition");// 轉換到最終狀態await contract.finalize();expect(await contract.state()).to.equal(States.FINALIZED);});
});

六、調試技巧

1. 使用console.log

// 在Solidity中使用console.log
pragma solidity ^0.8.0;import "hardhat/console.sol";contract MyContract {function testFunction() public {console.log("Value is:", value);console.log("Sender is:", msg.sender);}
}

2. 詳細的錯誤信息

// 在測試中獲取詳細的錯誤信息
it("Should provide detailed error messages", async function () {try {await contract.failingFunction();expect.fail("Expected function to revert");} catch (error) {// 解析詳細的錯誤信息expect(error.message).to.include("Custom error message");console.log("Full error:", error);}
});

通過以上詳細的測試庫解析和示例,您可以構建全面、可靠的智能合約測試套件,確保合約的安全性和正確性。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/922423.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/922423.shtml
英文地址,請注明出處:http://en.pswp.cn/news/922423.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

【大模型算法工程師面試題】大模型領域新興的主流庫有哪些?

文章目錄 大模型領域新興主流庫全解析:國產化適配+優劣對比+選型指南(附推薦指數) 引言 一、總覽:大模型工具鏈選型框架(含推薦指數) 二、分模塊詳解:優劣對比+推薦指數+選型建議 2.1:訓練框架(解決“千億模型怎么訓”) 2.2:推理優化(解決“模型跑起來慢”) 2.3:…

端口打開與服務可用

端口打開與服務可用“端口已打開但服務不可用” 并非矛盾&#xff0c;而是網絡訪問中常見的分層問題。要理解這一點&#xff0c;需要先明確 “端口打開” 和 “服務可用” 的本質區別&#xff1a;1. 什么是 “端口打開”&#xff1f;“端口打開” 通常指 操作系統的網絡層監聽該…

ByteDance_FrontEnd

約面了&#xff0c;放輕松&#xff0c;好好面 盲點 基礎知識 Function 和 Object 都是函數&#xff0c;而函數也是對象。 Object.prototype 是幾乎所有對象的原型鏈終點&#xff08;其 proto 是 null&#xff09;。 Function.prototype 是所有函數的原型&#xff08;包括 Obje…

go語言,彩色驗證碼生成,加減法驗證,

代碼結構相關代碼 captcha/internal/captcha/generator.go package captchaimport (_ "embed" // &#x1f448; 啟用 embed"image""image/color""image/draw""image/png""io""math/rand""golang.…

PuTTY軟件訪問ZYNQ板卡的Linux系統

PuTTY 是一款非常經典、輕量級、免費的 SSH、Telnet 和串行端口連接客戶端&#xff0c;主要運行于 Windows 平臺。它是在開源許可下開發的&#xff0c;因其小巧、簡單、可靠而成為系統管理員、網絡工程師和開發人員的必備工具。網上有非常多的下載資源。 我們使用PuTTY軟件對ZY…

做一個RBAC權限

在分布式應用場景下&#xff0c;我們可以利用網關對請求進行集中處理&#xff0c;實現了低耦合&#xff0c;高內聚的特性。 登陸權限驗證和鑒權的功能都可以在網關層面進行處理&#xff1a; 用戶登錄后簽署的jwt保存在header中&#xff0c;用戶信息則保存在redis中網關應該對不…

【算法】day1 雙指針

1、移動零&#xff08;同向分3區域&#xff09; 283. 移動零 - 力扣&#xff08;LeetCode&#xff09; 題目&#xff1a; 思路&#xff1a;注意原地操作。快排也是這個方法&#xff1a;左邊小于等于 tmp&#xff0c;右邊大于 tmp&#xff0c;最后 tmp 放到 dest。 代碼&#…

Linux 日志分析:用 ELK 搭建個人運維監控平臺

Linux 日志分析&#xff1a;用 ELK 搭建個人運維監控平臺 &#x1f31f; Hello&#xff0c;我是摘星&#xff01; &#x1f308; 在彩虹般絢爛的技術棧中&#xff0c;我是那個永不停歇的色彩收集者。 &#x1f98b; 每一個優化都是我培育的花朵&#xff0c;每一個特性都是我放飛…

Linux網絡:socket編程UDP

文章目錄前言一&#xff0c;socket二&#xff0c;服務端socket3-1 創建socket3-2 綁定地址和端口3-3 接收數據3-4 回復數據3-5關閉socket3-6 完整代碼三&#xff0c;客戶端socket3-1 為什么客戶端通常不需要手動定義 IP 和端口前言 學習 socket 編程的意義在于&#xff1a;它讓…

【從零到公網】本地電腦部署服務并實現公網訪問(IPv4/IPv6/DDNS 全攻略)

從零到公網&#xff1a;本地電腦部署服務并實現公網訪問&#xff08;IPv4/IPv6/DDNS 全攻略&#xff09; 適用場景&#xff1a;本地 API 服務、大模型推理服務、NAS、遠程桌面等需要公網訪問的場景 關鍵詞&#xff1a;公網 IP、端口映射、內網穿透、IPv6、Cloudflare DDNS 一、…

模塊二 落地微服務

11 | 服務發布和引用的實踐 服務發布和引用常見的三種方式&#xff1a;Restful API、XML配置以及IDL文件。今天我將以XML配置方式為例&#xff0c;給你講解服務發布和引用的具體實踐以及可能會遇到的問題。 XML配置方式的服務發布和引用流程 1. 服務提供者定義接口 服務提供者發…

C++程序員速通C#:從Hello World到數據類型

C程序員光速入門C#&#xff08;一&#xff09;&#xff1a;總覽、數據類型、運算符 一.Hello world&#xff01; 隨著.NET的深入人心,作為一個程序員&#xff0c;當然不能在新技術面前停而止步&#xff0c;面對著c在.net中的失敗,雖然有一絲遺憾&#xff0c;但是我們應該認識到…

Linux相關概念和易錯知識點(44)(IP地址、子網和公網、NAPT、代理)

目錄1.IP地址&#xff08;1&#xff09;局域網和公網①局域網a.網關地址b.局域網通信②運營商子網③公網&#xff08;2&#xff09;NAPT①NAPT過程②理解NAPT③理解源IP和目的IPa.目的IPb.源IP③最長前綴匹配④NAT技術缺陷2.代理服務&#xff08;1&#xff09;正向代理&#xf…

工業智能終端賦能自動化生產線建設數字化管理

在當今數字化浪潮的推動下&#xff0c;自動化生產線正逐漸成為各行各業提升效率和降低成本的重要選擇。隨著智能制造的深入發展&#xff0c;工業智能終端的引入不僅為生產線帶來了技術革新&#xff0c;也賦予了數字化管理新的動力。一、工業智能終端&#xff1a;一體化設計&…

【Vue2手錄06】計算屬性Computed

一、表單元素的v-model綁定&#xff08;核心場景&#xff09; v-model 是Vue實現“表單元素與數據雙向同步”的語法糖&#xff0c;不同表單元素的綁定規則存在差異&#xff0c;需根據元素類型選擇正確的綁定方式。 1.1 四大表單元素的綁定規則對比表單元素類型綁定數據類型核心…

FPGA入門-數碼管靜態顯示

19. 數碼管的靜態顯示 在許多項目設計中&#xff0c;我們通常需要一些顯示設備來顯示我們需要的信息&#xff0c;可以選擇的顯示設備有很多&#xff0c;而數碼管是使用最多&#xff0c;最簡單的顯示設備之一。數碼管是一種半導體發光器件&#xff0c;具有響應時間短、體積小、…

深入理解大語言模型(5)-關于token

到目前為止對 LLM 的描述中&#xff0c;我們將其描述為一次預測一個單詞&#xff0c;但實際上還有一個更重要的技術細 節。即 LLM 實際上并不是重復預測下一個單詞&#xff0c;而是重復預測下一個 token 。對于一個句子&#xff0c;語言模型會 先使用分詞器將其拆分為一個個 to…

視覺智能的「破壁者」——Transformer如何重塑計算機視覺范式?三大CV算法論文介紹 ViTMAESwin Transformer

當自然語言處理領域因Transformer而煥發新生時&#xff0c;計算機視覺卻長期困于卷積神經網絡的架構桎梏。直到ViT&#xff08;Vision Transformer&#xff09;的橫空出世&#xff0c;才真正打破了視覺與語言之間的壁壘。它不僅是技術的革新&#xff0c;更是范式革命的開始&…

Java 并發容器源碼解析:ConcurrentSkipListSet 行級深度剖析

Java 并發容器源碼解析&#xff1a;ConcurrentSkipListSet 行級深度剖析 本文將深入解析 Java 并發容器 ConcurrentSkipListSet 的核心源碼&#xff0c;結合流程圖、代碼注釋、設計思想、優缺點分析、業務場景、調試與優化、集成方案、高階應用等&#xff0c;幫助你系統掌握這款…

答題卡自動識別案例

目錄 1.答題卡自動批閱整體實現思路 2.關鍵技術步驟與原理 答題卡區域提取 ①輪廓檢測并排序 ②執行透視變換 ③找到每一個圓圈輪廓 ④先對所有圓圈輪廓從上到下排序 ⑤再通過循環每次只提取出五個輪廓再進行從左到右的排序 3.完整代碼 1.答題卡自動批閱整體實現思路 …