Write contract test cases based on Hardhat

Write contract test cases based on Hardhat

Writing automated tests for smart contracts is crucial, after all, writing smart contracts is more or less tied to user funds.

Scenes

It is assumed here that I am developing an NFT trading platform. This platform allows users to sell their own NFTs, including ERC721 and ERC1155, and users can specify that buyers need to pay for specified purchases ERC20 Token.
We first determine our test function and goal. In order not to make the article too long, we will selltest it with the function of calling and creating sales orders by seller users.

contract code

We need 4 contract files:

  1. ERC20
  2. ERC721
  3. ERC1155
  4. NFTSwap (trading platform)

The first three contracts are the simplest, we don’t need to implement them ourselves, we just need to directly quote the contract code of Openzeppelin. Create a new file in the directory
and paste the following code into itcontractsTestDependency.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";

In this way, the ERC20, ERC721, and ERC1155 contracts that need to be used will be compiled into the project

I only show sellthe relevant part of the NFTSwap contract code, enough to test it. Create a new contract
in the directory and paste the following codecontractsNFTSwap.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) {
    
    
        // 创建售卖订单逻辑
        //.......
    }

Compile the contract

➜ npx hardhat compile
Compiled 36 Solidity files successfully

The contract is compiled and passed, the next step

Reference Test Kit

Modify the project root directory hardhat.config.jsand add a reference to the toolkit

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);
  }
});

write test code

This part is the key point. I will split and explain the entire test script file first, and attach the completed code at the end of the article.

quote

testCreate a new file in the directory , sell-test.jswhere we will edit the test case code
and add references first

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合约测试

Define test suites and global variables

Because I will define multiple test cases in this suite to simulate multiple scenarios, global variables can be defined to reduce code duplication

describe("Test NFTSwap.sell Interface", function () {
    
    
    var ERC20; 	// 存放要用到的ERC20
    var ERC721;	// 同上
    var ERC1155; // 同上
    var OWNER; // 这里是为了演示模拟多用户操作 
    var ADDR1; // 同上
}

definitionbeforeEach

beforeEachwill be run before each test case is run. beforeEachThe environment can be initialized before each test by definition , so that the data of multiple test cases will not affect each other, because beforeEachthe environment will be reset before each test case is run

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'
    });
});

Define test cases

Here I will define three test cases, simulating the sale of different types of NFTs, and the sale of two NFTs at the same time

first test case

1个ERC721 TokenSuccessfully created sales order

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);
});

second test case

1个ERC1155T okenSuccessfully created sales order

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);
});

third test case

Create sales 1个ERC721 Token+ 1个ERC1155T okenorder success

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);
});

At this point, our test script file has been completed, and then run the test script directly to view the test results

run test script

➜  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)

Here you can see that the tests pass

Complete test script code

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);
    });
});

If you have any questions or suggestions, please leave a message, thank you.

Guess you like

Origin blog.csdn.net/Lyon_Nee/article/details/125559468