当前位置:网站首页>基于Hardhat编写合约测试用例
基于Hardhat编写合约测试用例
2022-07-02 09:38:00 【灬倪先森_】
基于Hardhat编写合约测试用例
为智能合约编写自动化测试至关重要,毕竟写智能合约多多少少都会跟用户资金挂钩。
场景
这里假设自己正在开发一个NFT交易平台,这个平台可以让用户售卖自己的NFT,包括ERC721和ERC1155,并且用户可以指定购买者需要支付指定的ERC20 Token
购买。
我们先确定自己的测试功能和目标,为了文章篇幅不要太长,我们就以卖家用户调用sell
,创建售卖订单功能为目标做测试。
合约代码
我们需要4个合约文件:
- ERC20
- ERC721
- ERC1155
- NFTSwap(交易平台)
前三种合约最简单的,我们不需要自己再去实现,直接引用Openzeppelin的合约代码即可。
在contracts
目录下创建一个新的文件TestDependency.sol
,并且把下面的代码粘贴进去
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;
import "@openzeppelin/contracts/token/ERC20/presets/ERC20PresetMinterPauser.sol";
import "@openzeppelin/contracts/token/ERC721/presets/ERC721PresetMinterPauserAutoId.sol";
import "@openzeppelin/contracts/token/ERC1155/presets/ERC1155PresetMinterPauser.sol";
这样需要用到的ERC20,ERC721,ERC1155合约就会被编译到项目中
NFTSwap合约代码我只展示sell
相关部分,足够测试即可
在contracts
目录下新建一个NFTSwap.sol
合约,并粘贴下面的代码
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;
import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
contract NFTSwap is Initializable {
enum AssetType {
ERC721,
ERC1155
}
struct Asset {
address Contract; // NFT Token地址
uint256 TokenId; // Token id
uint256 TokenValue; // Token Value, ERC721 为1
AssetType Type; // NFT 类型
}
function __NFTSwap_init() public initializer {
}
function sell(
Asset[] calldata assets, // 要售卖的NFT,可以同时售卖多个
address paymentToken, // 指定接受购买支付的 ERC20 代币
uint256 price // 售卖价格
) public virtual returns (uint256 goodsId) {
// 创建售卖订单逻辑
//.......
}
编译合约
* npx hardhat compile
Compiled 36 Solidity files successfully
合约编译通过,下一步
引用测试工具包
修改项目根目录下的hardhat.config.js
,添加对工具包的引用
require("@nomiclabs/hardhat-waffle");
require('@openzeppelin/hardhat-upgrades');
task("accounts", "Prints the list of accounts", async (taskArgs, hre) => {
const accounts = await hre.ethers.getSigners();
for (const account of accounts) {
console.log(account.address);
}
});
编写测试代码
这一部分是重点,我会把整个测试脚本文件先拆分讲解,并在文章最后附上完成的代码
引用
在test
目录下新建sell-test.js
文件,我们将在这里编辑测试用例代码
先添加引用
const {
expect, use } = require('chai'); //引入断言库
const {
BigNumber } = require('ethers'); // bignumber一会儿要用到
const {
deployContract, MockProvider, solidity } = require('ethereum-waffle');
const {
ethers, upgrades } = require("hardhat");
use(solidity); // 这里是跟 chai 声明使用在solidity合约测试
定义测试套件和全局变量
因为我会在这个套件内定义多个测试用例,模拟多种场景,所以可以定义全局变量,减少代码重复
describe("Test NFTSwap.sell Interface", function () {
var ERC20; // 存放要用到的ERC20
var ERC721; // 同上
var ERC1155; // 同上
var OWNER; // 这里是为了演示模拟多用户操作
var ADDR1; // 同上
}
定义beforeEach
beforeEach
会在每个测试用例运行前先运行。可以通过定义beforeEach
在每次测试前初始化环境,这样可以做到多个测试用例的数据不会相互影响,因为每次运行用例前,beforeEach
都会重置环境
beforeEach(async () => {
// 模拟不同的两个用户,比如测试完成的买卖流程就应该用 两个用户地址
[OWNER, ADDR1] = await ethers.getSigners();
// Owner 用户创建多个合约
const ERC20PresetMinterPauser = await ethers.getContractFactory("ERC20PresetMinterPauser", OWNER);
ERC20 = await ERC20PresetMinterPauser.deploy("TestERC20", "T20");
const ERC721PresetMinterPauserAutoId = await ethers.getContractFactory("ERC721PresetMinterPauserAutoId", OWNER);
ERC721 = await ERC721PresetMinterPauserAutoId.deploy("TestERC721", "T721", "https://t721.com");
const ERC1155PresetMinterPauser = await ethers.getContractFactory("ERC1155PresetMinterPauser", OWNER);
ERC1155 = await ERC1155PresetMinterPauser.deploy("https://t1155.com");
const NFTSwap = await ethers.getContractFactory("NFTSwap");
NFT_SWAP = await upgrades.deployProxy(NFTSwap, [], {
initializer: '__NFTSwap_init'
});
});
定义测试用例
这里我会定义三个测试用例,模拟售卖不同种类NFT,和同时售卖两种NFT的情况
第一个测试用例
创建售卖1个ERC721 Token
订单成功
it("Should be sale an ERC721 token successful", async function () {
// 确定 NFTSwap合约 部署完成
await NFT_SWAP.deployed();
// 确定 ERC721合约 部署完成
await ERC721.deployed();
// 增发 id=0 的token,并approve 给 NFTSwap
var mintERC721Tx = await ERC721.connect(OWNER).mint(OWNER.address);
await mintERC721Tx.wait();
var approveERC721Tx = await ERC721.connect(OWNER).setApprovalForAll(NFT_SWAP.address, true);
await approveERC721Tx.wait();
// 定义assets, assetType.ERC721 = 1
var assets = [{
Contract: ERC721.address, TokenId: BigNumber.from(0), TokenValue: BigNumber.from(1), Type: 1 }]
await ERC20.deployed();
// 发起交易
const sellTx = await NFT_SWAP.sell(assets, ERC20.address, BigNumber.from(1000000));
await sellTx.wait()
// 获取交易结果
var receipt = await ethers.provider.getTransactionReceipt(sellTx.hash);
// 判断交易最终状态,必须为1,1表示合约执行成功
expect(receipt.status).to.equal(1);
});
第二个测试用例
创建售卖1个ERC1155T oken
订单成功
it("Should be sale an ERC1155 token successful", async function () {
// 确定 NFTSwap合约 部署完成
await NFT_SWAP.deployed();
// 确定 ERC1155合约 部署完成
await ERC1155.deployed();
// 增发 id=0 的token,并approve 给 NFTSwap
var mintERC1155Tx = await ERC1155.connect(OWNER).mint(OWNER.address, 1, 10, "0x");
await mintERC1155Tx.wait();
var approveERC1155Tx = await ERC1155.connect(OWNER).setApprovalForAll(NFT_SWAP.address, true);
await approveERC1155Tx.wait();
// 定义assets, assetType.ERC1155 = 2
var assets = [{
Contract: ERC1155.address, TokenId: BigNumber.from(1), TokenValue: BigNumber.from(1), Type: 2 }]
await ERC20.deployed();
const sellTx = await NFT_SWAP.sell(assets, ERC20.address, BigNumber.from(1000000));
await sellTx.wait()
var receipt = await ethers.provider.getTransactionReceipt(sellTx.hash);
expect(receipt.status).to.equal(1);
});
第三个测试用例
创建售卖 1个ERC721 Token
+ 1个ERC1155T oken
订单成功
it("Should be packet sale an ERC721 token and an ERC1155 token successful", async function () {
// 确定 NFTSwap合约 部署完成
await NFT_SWAP.deployed();
// 确定 ERC721合约 部署完成
await ERC721.deployed();
// 增发 id=0 的ERC721 token,并approve 给 NFTSwap
var mintERC721Tx = await ERC721.connect(OWNER).mint(OWNER.address);
await mintERC721Tx.wait();
var approveERC721Tx = await ERC721.connect(OWNER).setApprovalForAll(NFT_SWAP.address, true);
await approveERC721Tx.wait();
// 确定 ERC1155合约 部署完成
await ERC1155.deployed();
// 增发 id=0 的ERC1155 token,并approve 给 NFTSwap
var mintERC1155Tx = await ERC1155.connect(OWNER).mint(OWNER.address, 1, 10, "0x");
await mintERC1155Tx.wait();
var approveERC1155Tx = await ERC1155.connect(OWNER).setApprovalForAll(NFT_SWAP.address, true);
await approveERC1155Tx.wait();
// 定义assets,这里是用两个 NFT Token的
var assets = [{
Contract: ERC721.address, TokenId: BigNumber.from(0), TokenValue: BigNumber.from(1), Type: 1 },
{
Contract: ERC1155.address, TokenId: BigNumber.from(1), TokenValue: BigNumber.from(10), Type: 2 }]
await ERC20.deployed();
const sellTx = await NFT_SWAP.sell(assets, ERC20.address, BigNumber.from(200000));
await sellTx.wait()
var receipt = await ethers.provider.getTransactionReceipt(sellTx.hash);
expect(receipt.status).to.equal(1);
});
到这里,我们的测试脚本文件已经完成了,接下来直接运行测试脚本,查看测试结果就可以了
运行测试脚本
* npx hardhat test test/sell-test.js
Test NFTSwap.sell Interface
Should be sale an ERC721 token successful (120ms)
Should be sale an ERC1155 token successful (99ms)
Should be packet sale an ERC721 token and an ERC1155 token successful (177ms)
3 passing (4s)
这里可以看到测试都通过
完整测试脚本代码
const {
expect, use } = require('chai');
const {
BigNumber } = require('ethers');
const {
deployContract, MockProvider, solidity } = require('ethereum-waffle');
const {
ethers, upgrades } = require("hardhat");
use(solidity);
describe("Test NFTSwap.sell Interface", function () {
var ERC20;
var ERC721;
var ERC1155;
var OWNER;
var ADDR1;
var NFT_SWAP;
beforeEach(async () => {
[OWNER, ADDR1] = await ethers.getSigners();
const ERC20PresetMinterPauser = await ethers.getContractFactory("ERC20PresetMinterPauser", OWNER);
ERC20 = await ERC20PresetMinterPauser.deploy("TestERC20", "T20");
const ERC721PresetMinterPauserAutoId = await ethers.getContractFactory("ERC721PresetMinterPauserAutoId", OWNER);
ERC721 = await ERC721PresetMinterPauserAutoId.deploy("TestERC721", "T721", "https://t721.com");
const ERC1155PresetMinterPauser = await ethers.getContractFactory("ERC1155PresetMinterPauser", OWNER);
ERC1155 = await ERC1155PresetMinterPauser.deploy("https://t1155.com");
const NFTSwap = await ethers.getContractFactory("NFTSwap");
NFT_SWAP = await upgrades.deployProxy(NFTSwap, {
initializer: '__NFTSwap_init'
});
});
it("Should be sale an ERC721 token successful", async function () {
await NFT_SWAP.deployed();
await ERC721.deployed();
var mintERC721Tx = await ERC721.connect(OWNER).mint(OWNER.address);
await mintERC721Tx.wait();
var approveERC721Tx = await ERC721.connect(OWNER).setApprovalForAll(NFT_SWAP.address, true);
await approveERC721Tx.wait();
var assets = [{
Contract: ERC721.address, TokenId: BigNumber.from(0), TokenValue: BigNumber.from(1), Type: 1 }]
await ERC20.deployed();
const sellTx = await NFT_SWAP.sell(assets, ERC20.address, BigNumber.from(1000000));
await sellTx.wait()
var receipt = await ethers.provider.getTransactionReceipt(sellTx.hash);
expect(receipt.status).to.equal(1);
});
it("Should be sale an ERC1155 token successful", async function () {
await NFT_SWAP.deployed();
await ERC1155.deployed();
var mintERC1155Tx = await ERC1155.connect(OWNER).mint(OWNER.address, 1, 10, "0x");
await mintERC1155Tx.wait();
var approveERC1155Tx = await ERC1155.connect(OWNER).setApprovalForAll(NFT_SWAP.address, true);
await approveERC1155Tx.wait();
var assets = [{
Contract: ERC1155.address, TokenId: BigNumber.from(1), TokenValue: BigNumber.from(1), Type: 2 }]
await ERC20.deployed();
const sellTx = await NFT_SWAP.sell(assets, ERC20.address, BigNumber.from(1000000));
await sellTx.wait()
var receipt = await ethers.provider.getTransactionReceipt(sellTx.hash);
expect(receipt.status).to.equal(1);
});
it("Should be packet sale an ERC721 token and an ERC1155 token successful", async function () {
await NFT_SWAP.deployed();
await ERC721.deployed();
var mintERC721Tx = await ERC721.connect(OWNER).mint(OWNER.address);
await mintERC721Tx.wait();
var approveERC721Tx = await ERC721.connect(OWNER).setApprovalForAll(NFT_SWAP.address, true);
await approveERC721Tx.wait();
await ERC1155.deployed();
var mintERC1155Tx = await ERC1155.connect(OWNER).mint(OWNER.address, 1, 10, "0x");
await mintERC1155Tx.wait();
var approveERC1155Tx = await ERC1155.connect(OWNER).setApprovalForAll(NFT_SWAP.address, true);
await approveERC1155Tx.wait();
var assets = [{
Contract: ERC721.address, TokenId: BigNumber.from(0), TokenValue: BigNumber.from(1), Type: 1 },
{
Contract: ERC1155.address, TokenId: BigNumber.from(1), TokenValue: BigNumber.from(10), Type: 2 }]
await ERC20.deployed();
const sellTx = await NFT_SWAP.sell(assets, ERC20.address, BigNumber.from(200000));
await sellTx.wait()
var receipt = await ethers.provider.getTransactionReceipt(sellTx.hash);
expect(receipt.status).to.equal(1);
});
});
有问题,或者建议请留言,谢谢。
边栏推荐
- A white hole formed by antineutrons produced by particle accelerators
- TIPC introduction 1
- MySQL basic statement
- Summary of cases of players' disconnection and reconnection in Huawei online battle service
- js中给数组添加元素的方法有哪些
- 微信小程序利用百度api达成植物识别
- SQLite modify column type
- PowerBI中导出数据方法汇总
- PKG package manager usage instance in FreeBSD
- QT learning diary 8 - resource file addition
猜你喜欢
Amazon cloud technology community builder application window opens
TIPC 寻址2
RPA advanced (II) uipath application practice
tidb-dm报警DM_sync_process_exists_with_error排查
[play with FPGA learning 5 in simple terms ----- reset design]
Openmldb meetup No.4 meeting minutes
[in simple terms, play with FPGA learning 3 ----- basic grammar]
在连接mysql数据库的时候一直报错
ImportError: cannot import name ‘Digraph‘ from ‘graphviz‘
【IDEA】使用插件一键逆向生成代码
随机推荐
Summary of data export methods in powerbi
通过券商经理的开户二维码开股票账户安全吗?还是去证券公司开户安全?
The first white paper on agile practice in Chinese enterprises was released | complete download is attached
八大排序汇总
解决uniapp列表快速滑动页面数据空白问题
Compilation errors and printout garbled problems caused by Chinese content in vs2019 code
ros缺少catkin_pkg
C# 文件与文件夹操作
Resources reads 2D texture and converts it to PNG format
TIPC protocol
ROS lacks catkin_ pkg
Attribute acquisition method and operation notes of C # multidimensional array
ros gazebo相关包的安装
III Chip startup and clock system
Thanos Receiver
TIPC协议
Calculate the sum of sequences
Why does LabVIEW lose precision in floating point numbers
TIPC介绍1
RPA advanced (II) uipath application practice