概述

智能合约测试库是区块链开发中至关重要的工具,用于确保智能合约的安全性、正确性和可靠性。以下是主流的智能合约测试库及其详细解析。

一、主流测试框架对比

测试框架开发语言主要特点适用场景
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.答题卡自动批阅整体实现思路 …