Solidity全局变量完全测试

Solidity全局变量完全测试

我们知道,在Solidity中有很多全局变量,例如我们最常用的msg.sender, block.timestamp等。但是有一些我们平常基本接触不到,例如:type(C).name等。本着凡事最怕认真两字的原则,虽然繁琐,但我们将所有的全局变量全部测试一遍,学习怎么调用和应用在哪些场景,进一步加深理解与记忆。

本文基于Solidity 0.8.9版本与hardhat工具进行,在最新的0.8.13版本增加了两个全局变量abi.encodeCallstring.concat,因当前版本的hardhat暂不支持 Solidity 0.8.13,故没有进行这两项测试。
另外,有少数项目也不方便测试,期待有人能改进完善测试方法。

测试合约

我们的测试合约如下:

// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.9;

interface IERC721 {
    
    
    function balanceOf(address _owner) external view returns (uint256);
    function ownerOf(uint256 _tokenId) external view returns (address);
    function safeTransferFrom(address _from, address _to, uint256 _tokenId, bytes calldata data) external payable;
    function safeTransferFrom(address _from, address _to, uint256 _tokenId) external payable;
    function transferFrom(address _from, address _to, uint256 _tokenId) external payable;
    function approve(address _approved, uint256 _tokenId) external payable;
    function setApprovalForAll(address _operator, bool _approved) external;
    function getApproved(uint256 _tokenId) external view returns (address);
    function isApprovedForAll(address _owner, address _operator) external view returns (bool);
}

contract NoReceiveEth {
    
    
    function originTest(address instance) public view returns(address) {
    
    
        return GlobalVariables(instance).originTest();
    }
}

contract Base {
    
    
    function getUint(uint a) public pure virtual returns(uint) {
    
    
        return a;
    }
}

/// 全局变量测试
contract GlobalVariables is Base{
    
    

    event SendEther(address indexed receipt,uint amount, bool result);
    event ReturnBaseFee(uint fee);
    event GasLeft(uint gas);
    event SelfDestruct(address indexed recipient);

    // 解码数据
    // 这里只适合类型提前知道的情况,因为Solidity类型无法作为参数,所以这里采用了固定类型进行测试
    function decodeTest(bytes memory data) public pure returns(uint a, uint[2] memory b, bytes memory c) {
    
    
        (a,b,c) = abi.decode(data,(uint, uint[2], bytes));
    }

    //编码数据,这里可以和解码数据联合测试
    function encodeTest(uint a, uint[2] memory b, bytes memory c) public pure returns(bytes memory) {
    
    
        return abi.encode(a,b,c);
    }

    //压缩编码,注意它会引起混淆,一般用于哈希计算时,也就是不需要解码操作
    function encodePackedTest(uint a, uint[2] memory b, bytes memory c) public pure returns(bytes memory) {
    
    
        return abi.encodePacked(a,b,c);
    }

    //根据函数选择器编码,其实就是函数选择器加上前面的编码,通常用于底层函数调用,例如SafeTransfer。
    function encodeWithSelectorTest(bytes4 selector,uint a, uint[2] memory b, bytes memory c) public pure returns(bytes memory) {
    
    
        return abi.encodeWithSelector(selector, a,b,c);
    }

    // 0.8.13版本才有,hardhat暂不支持这么高版本,故skip
    // function(uint, uint[2] memory, bytes memory ) functionPointer;
    // //直接根据函数编码,其结果和encodeWithSelector相同,但会多一个参数类型检查
    // function encodeCallTest(
    //     uint a, 
    //     uint[2] memory b, 
    //     bytes memory c
    // ) public pure returns(bytes memory) {
    
    
    //     return abi.encodeCall(functionPointer,a,b,c);
    // }


    //根据函数名称编码,这里其实给出了函数选择器的计算方法: bytes4(keccak256(bytes(signature))
    // 结果和 abi.encodeWithSelector(bytes4(keccak256(bytes(signature)), ...) 相同
    // 注意signature的写法:例如: "testFunc(uint256,uint256[2],bytes)"
    function encodeWithSignatureTest(
        string memory signature,
        uint a, 
        uint[2] memory b, 
        bytes memory c
    ) public pure returns(bytes memory) {
    
    
        return abi.encodeWithSignature(signature,a,b,c);
    }

    // 连接bytes 、bytes1 - bytes32,注意,它并不会padding
    function bytesConcatTest(bytes memory a, bytes memory b, bytes memory c)  public pure returns(bytes memory) {
    
    
        return bytes.concat(a,b,c);
    }


    // //字符串连接,很直观 0.8.13版本才有,hardhat暂不支持这么高版本,故skip
    // function stringConcatTest(string memory a, string memory b, string memory c) public pure returns(string memory) {
    
    
    //     return string.concat(a,b,c);
    // }

    // EIP-1559 基础fee 
    function basefeeTest() public returns(uint) {
    
    
        emit ReturnBaseFee(block.basefee);
        return block.basefee;
    }

    // 以下是获取各种区块信息
    function chainidTest() public view returns(uint) {
    
    
        return block.chainid;
    }

    function coinbaseTest() public view returns(address payable) {
    
    
        return block.coinbase;
    }

    function difficultyTest() public view returns(uint) {
    
    
        return block.difficulty;
    }

    function gaslimitTest() public view returns(uint) {
    
    
        return block.gaslimit;
    }

    function numberTest() public view returns(uint) {
    
    
        return block.number;
    }

    function timestampTest() public view returns(uint) {
    
    
        return block.timestamp;
    }

    // 不是很好测试
    function gasleftTest() public returns(uint) {
    
    
        uint gas = gasleft();
        emit GasLeft(gas);
        return gas;
    }

    function msgDataTest(uint , uint[2] memory , bytes memory ) public pure returns(bytes memory) {
    
    
        return msg.data;
    }

    function msgSenderTest() public view returns(address) {
    
    
        return msg.sender;
    }

    function msgSigTest(uint , uint[2] memory , bytes memory ) public pure returns(bytes4) {
    
    
        return msg.sig;
    }

    function msgValueTest() payable public returns(uint) {
    
    
        emit SendEther(address(this),msg.value,true);
        return msg.value;
    }

    function gasPriceTest() public view returns(uint) {
    
    
        return tx.gasprice;
    }

    function originTest() public view returns(address) {
    
    
        require(msg.sender == tx.origin, "Sender is a contract");
        return tx.origin;
    }

    // 抛出异常
    function assertTest(uint a) public pure returns(bool) {
    
    
        assert(a > 5);
        return true;
    }

    function requireTest(uint a) public pure returns(bool) {
    
    
        require(a > 5);
        return true;
    }

    function requireTest(uint a , string memory reason) public pure returns(bool) {
    
    
        require(a > 5 , reason);
        return true;
    }

    function revertTest(bool isRevert) public pure returns(bool) {
    
    
        if(isRevert) {
    
    
            revert();
        }
        return true;
    }

    function revertTest(bool isRevert, string memory reason) public pure returns(bool) {
    
    
        if(isRevert) {
    
    
            revert(reason);
        }
        return true;
    }

    // 加密算法
    function blockhashTest(uint blockNumber) public view returns(bytes32) {
    
    
        return blockhash(blockNumber);
    }

    function keccak256Test(bytes memory data) public pure returns(bytes32) {
    
    
        return keccak256(data);
    }

    function sha256Test(bytes memory data) public pure returns(bytes32) {
    
    
        return sha256(data);
    }

    function ripemd160Test(bytes memory data) public pure returns(bytes20) {
    
    
        return ripemd160(data);
    }

    // 注意这里第一个参数为messageHash,不是message
    function ecrecoverTest(bytes32 hash, uint8 v, bytes32 r, bytes32 s) public pure returns(address) {
    
    
        return ecrecover(hash,v,r,s);
    }

    // 注意这里重点是可溢出
    function addmodTest(uint x, uint y, uint k) public pure returns(uint) {
    
    
        return addmod(x,y,k);
    }

    function mulmodTest(uint x, uint y, uint k) public pure returns(uint) {
    
    
        return mulmod(x, y, k);
    }

    // this 代表本合约
    function thisTest() public view returns(address) {
    
    
        return address(this);
    }

    // 注意 this.call代表一个外部调用而不是内部跳转
    function thisCallTest() public view returns(address) {
    
    
        return this.msgSenderTest();
    }

    // 调用父合约函数
    function superTest(uint a) public pure returns(uint) {
    
    
        if(a > 10) {
    
    
            return super.getUint(a);
        } else {
    
    
            return a - 2;
        }
    }

    // 自杀,注意这里转移ETH会无视合约是否接收ETH
    function selfdestructTest(address payable recipient) public {
    
    
        emit SelfDestruct(recipient);
        selfdestruct(recipient);
        // 注意,这后面的语句可能无法执行
    }

    function balanceTest() public view returns(uint) {
    
    
        return address(this).balance;
    }

    //这里的code不知道该怎么测试
    function codeTest() public view returns(bytes memory) {
    
    
        return address(this).code;
    }

    function codehashTest() public view returns(bytes32) {
    
    
        return address(this).codehash;
    }

    // 注意发送失败返回falses
    function sendTest(address payable recipient, uint amount) public returns(bool result) {
    
    
        result = recipient.send(amount);
        emit SendEther(recipient,amount,result);
    }

    // 发送失败会重置
    function transferTest(address payable recipient, uint amount) public {
    
    
        recipient.transfer(amount);
        emit SendEther(recipient,amount,true);
    }

    // 下面的信息编译后可获取
    function contractNameTest() public pure returns(string memory) {
    
    
        return type(GlobalVariables).name;
    }

    function creationCodeTest() public pure returns(bytes memory) {
    
    
        return type(Base).creationCode;
    }

    function runtimeCodeTest() public pure returns(bytes memory) {
    
    
        return type(Base).runtimeCode;
    }

    /**
    *    0x70a08231 ^ 0x6352211e ^ 0x095ea7b3 ^ 0x081812fc ^
         0xa22cb465 ^ 0xe985e9c ^ 0x23b872dd ^ 0x42842e0e ^ 0xb88d4fde = 0x80ac58cd
     */
    function interfaceIdTest() public pure returns(bytes4) {
    
    
        return type(IERC721).interfaceId; // 0x80ac58cd
    }

    // 最小值,最大值,常用来替代uint(-1)
    function minTest() public pure returns(int256) {
    
    
        return type(int256).min;
    }

    function maxTest() public pure returns(uint256) {
    
    
        return type(uint).max;
    }
}

合约中部分知识点有简单注释。现在合约有了,我们要开始调用了,当然单元测试是最适合这项工作的。

单元测试文件

因为测试的内容较多,我们新建了两个单元测试文件
GlobalVariables-01.jsGlobalVariables-02.js

// GlobalVariables-01.js
const {
    
     expect, assert } = require("chai");
const {
    
     ethers } = require("hardhat");

describe("GlobalVariables Test 01", function () {
    
    

    let instance;
    let owner,user,addrs;
    let test_contract;

    const abi = [
        "function testFunc(uint a, uint[2] memory b, bytes memory c) public view returns(uint256)" 
    ]
    const interface = new ethers.utils.Interface(abi);

    const args = [
        10245,
        [2345,1098],
        "0x12345678"
    ]
    const abiCode = new ethers.utils.AbiCoder();

    before( async function() {
    
    
        const GlobalVariables = await ethers.getContractFactory("GlobalVariables");
        instance = await GlobalVariables.deploy();

        const NoReceiveEth = await ethers.getContractFactory("NoReceiveEth");
        test_contract = await NoReceiveEth.deploy();

        [owner, user, ...addrs] = await ethers.getSigners();
    });

    describe("Encode and Decode test", function() {
    
    
        it("Encode test", async () => {
    
    
            let data = abiCode.encode(
                ["uint","uint[2]","bytes"],
                args
            )
            expect(await instance.encodeTest(...args)).to.be.equal(data)
        })

        it("Decode test", async () => {
    
    
            let data = abiCode.encode(
                ["uint","uint[2]","bytes"],
                args
            )
            const [a,b,c] = await instance.decodeTest(data);
            assert.equal(a,args[0])
            assert.equal(b[0],args[1][0])
            assert.equal(b[1],args[1][1])
            assert.equal(c,args[2])
        });

        it("EncodePacked test", async () => {
    
    
            let data = abiCode.encode(
                ["uint","uint[2]"],
                [args[0],args[1]]
            )
            data += args[2].substring(2,args[2].length);
            expect (await instance.encodePackedTest(...args)).to.be.equal(data);
        })

        it("EncodeWithSelector test", async () => {
    
    
            const selector = interface.getSighash("testFunc");
            let data = interface.encodeFunctionData("testFunc",args);
            expect (await instance.encodeWithSelectorTest(selector,...args)).to.be.equal(data);
            let encode = await instance.encodeTest(...args);
            encode = encode.substring(2,encode.length);  //去掉0x显示
            assert.equal(selector + encode, data);  
        });

        it("EncodeWithSignature test", async () => {
    
    
            const selector = interface.getSighash("testFunc");
            let encode = await instance.encodeTest(...args);
            encode = encode.substring(2,encode.length);  //去掉0x显示
            expect(await instance.encodeWithSignatureTest(
                "testFunc(uint256,uint256[2],bytes)",
                ...args
            )).to.be.equal(selector + encode);
        })
    });

    describe("bytesConcat test", () => {
    
    
        it("Hello world test", async () => {
    
    
            const hello = ethers.utils.toUtf8Bytes("hello")
            const blank = ethers.utils.toUtf8Bytes(" ")
            const world = ethers.utils.toUtf8Bytes("world")
            let hello_world = ethers.utils.hexlify(
                ethers.utils.toUtf8Bytes("hello world")
            );
            expect (await instance.bytesConcatTest(hello,blank,world)).to.be.equal(hello_world);
        })
    })

    describe("Block Info test", () => {
    
    
        /**
         * 这里有瑕疵
         * 如果直接使用npx hardhat test ,这里的baseFee是会逐渐减小的
         * 但是如果使用npx hardhat coverage,这里的baseFee会是0,应该不正确。
         */
        it("Basefee test", async () => {
    
    
            let tx = await instance.basefeeTest({
    
    
                gasLimit:300000
            });
            let receipt = await tx.wait();
            let fee = receipt.events[0].args.fee;
            await expect(instance.basefeeTest({
    
    
                gasLimit:300000
            })).to.emit(instance,"ReturnBaseFee").withArgs(fee);
        });

        it("chainid test", async () => {
    
    
            let chainId = ethers.provider.network.chainId;
            expect(await instance.chainidTest()).to.be.equal(chainId);
        });

        it("coinbase test", async () => {
    
    
            let block = await ethers.provider.getBlock();
            expect(await instance.coinbaseTest()).to.be.equal(block.miner);
        })

        it("Difficulty test", async () => {
    
    
            let block = await ethers.provider.getBlock();
            expect(await instance.difficultyTest()).to.be.equal(block.difficulty);
        })

        it("Gaslimit test", async () => {
    
    
            let block = await ethers.provider.getBlock();
            expect(await instance.gaslimitTest()).to.be.equal(block.gasLimit);
        })

        it("Block number test", async () =>  {
    
    
            let block = await ethers.provider.getBlock();
            expect(await instance.numberTest()).to.be.equal(block.number);
        })

        it("Timestamp test", async () =>  {
    
    
            let block = await ethers.provider.getBlock();
            expect(await instance.timestampTest()).to.be.equal(block.timestamp);
        })

        it("Gasleft test", async () => {
    
    
            let tx = await instance.gasleftTest({
    
    
                gasLimit:300000
            });
            let receipt = await tx.wait();
            let gas = receipt.events[0].args.gas;
            await expect(instance.gasleftTest({
    
    
                gasLimit:300000
            })).to.emit(instance,"GasLeft").withArgs(gas)
        });

        it("BlockHashTest", async () => {
    
    
            let blockNumber = await ethers.provider.getBlockNumber();
            let curBlock = await ethers.provider.getBlock(blockNumber)
            let prevBlock = await ethers.provider.getBlock(blockNumber -1)
            assert.equal(curBlock.parentHash,prevBlock.hash)  // block chain
            expect(await instance.blockhashTest(blockNumber -1)).to.be.equal(prevBlock.hash)
        });
    });

    describe("Msg test", () => {
    
    
        it("Msg data test", async () => {
    
    
            let data =  instance.interface.encodeFunctionData("msgDataTest",args);
            expect(await instance.msgDataTest(...args)).to.be.equal(data)
        });

        it("Msg sender test", async () => {
    
    
            expect(await instance.msgSenderTest()).to.be.equal(owner.address)
            expect(await instance.connect(user).msgSenderTest()).to.be.equal(user.address)
        });

        it("Msg Sig test", async () => {
    
    
            let selector = instance.interface.getSighash("msgSigTest");
            expect(await instance.msgSigTest(...args)).to.be.equal(selector)
        });

        it("Msg value test", async () => {
    
    
            await expect(instance.msgValueTest({
    
    
                value:ethers.utils.parseEther("1.0")
            })).to.emit(instance,"SendEther").withArgs(instance.address,ethers.utils.parseEther("1.0"),true)
        });

        it("Gas price test", async () => {
    
    
            expect(await instance.gasPriceTest({
    
    
                gasPrice:500
            })).to.be.equal(500)
        });

        it("OriginTest successful", async () => {
    
    
            expect(await instance.originTest()).to.be.equal(owner.address);
        });

        it("OriginTest failed", async () => {
    
    
            await expect(test_contract.originTest(instance.address)).to.be.revertedWith("Sender is a contract")
        })
    });

    describe("Error deal test", () => {
    
    
        it("assert test", async () => {
    
    
            expect(await instance.assertTest(10)).to.be.true;
            await expect(instance.assertTest(3)).to.be.reverted;
        })

        it("require test", async () => {
    
    
            expect(await instance["requireTest(uint256)"](10)).to.be.true;
            await expect(instance["requireTest(uint256)"](3)).to.be.reverted;
            expect(await instance["requireTest(uint256,string)"](10,"less than 5")).to.be.true;
            await expect(instance["requireTest(uint256,string)"](3,"less than 5")).to.be.revertedWith("less than 5");
        })

        it("revert test", async () => {
    
    
            expect(await instance["revertTest(bool)"](false)).to.be.true;
            await expect(instance["revertTest(bool)"](true)).to.be.reverted;
            expect(await instance["revertTest(bool,string)"](false,"some reason")).to.be.true;
            await expect(instance["revertTest(bool,string)"](true,"some reason")).to.be.revertedWith("some reason");
        })
    });

    describe("Crypto test", () => {
    
    
        it("keccak256 test", async () => {
    
    
            let data = ethers.utils.toUtf8Bytes("hello world");
            expect(await instance.keccak256Test(data)).to.be.equal(ethers.utils.keccak256(data))
        });

        it("sha256 test", async () => {
    
    
            let data = ethers.utils.toUtf8Bytes("hello world");
            expect(await instance.sha256Test(data)).to.be.equal(ethers.utils.sha256(data))
        });

        it("ripemd160 test", async () => {
    
    
            let data = ethers.utils.toUtf8Bytes("hello world");
            expect(await instance.ripemd160Test(data)).to.be.equal(ethers.utils.ripemd160(data))
        });

        it("ecrecover test", async () => {
    
    
            let message = ethers.utils.solidityKeccak256(
                ["uint256","uint256[]","bytes"],
                args
            )
            let messageHash = ethers.utils.hashMessage(ethers.utils.arrayify(message))
            let signature = await user.signMessage(ethers.utils.arrayify(message))
            const {
    
    r,s,v} = ethers.utils.splitSignature(signature)
            expect(await instance.ecrecoverTest(messageHash,v,r,s)).to.be.equal(user.address)
        });
    });

    describe("math operation test", () => {
    
    
        it("add mod test", async () => {
    
    
            let x  = ethers.constants.MaxUint256;
            let y = ethers.constants.Two;
            let k = 10;
            // x + y = 115792089237316195423570985008687907853269984665640564039457584007913129639937
            expect(await instance.addmodTest(x,y,k)).to.be.equal(7);
        });
        it("mul mod test", async () => {
    
    
            let x  = ethers.constants.MaxUint256;
            let y = ethers.constants.Two;
            let k = 10;
            // x * y = 231584178474632390847141970017375815706539969331281128078915168015826259279870
            expect(await instance.mulmodTest(x,y,k)).to.be.equal(0);
        });
    });

    describe("Inheritance test", () => {
    
    
        it("this test", async () => {
    
    
            expect(await instance.thisTest()).to.be.equal(instance.address)
        });

        it("thisCall test", async () => {
    
    
            expect(await instance.thisCallTest()).to.be.equal(instance.address)
        });

        it("super test", async () => {
    
    
            expect(await instance.superTest(5)).to.be.equal(3);
            expect(await instance.superTest(13)).to.be.equal(13);
        });
    });
    
});
// GlobalVariables-02.js
const {
    
     expect, assert } = require("chai");
const {
    
     ethers } = require("hardhat");
const jsonData = require("../artifacts/contracts/GlobalVariables.sol/Base.json")

describe("GlobalVariables Test 01", function () {
    
    

    let instance;
    let owner,user,addrs;
    let test_contract;

    beforeEach( async function() {
    
    
        const GlobalVariables = await ethers.getContractFactory("GlobalVariables");
        instance = await GlobalVariables.deploy();

        const NoReceiveEth = await ethers.getContractFactory("NoReceiveEth");
        test_contract = await NoReceiveEth.deploy();

        [owner, user, ...addrs] = await ethers.getSigners();
    });

    describe("Contract test", () => {
    
    
        it("selfdestruct and balance test", async () => {
    
    
            // 1 send ether
            await instance.msgValueTest({
    
    
                value:ethers.utils.parseEther("1.0")
            });
            expect(await instance.balanceTest()).to.be.equal(ethers.utils.parseEther("1.0"));
            expect(await ethers.provider.getBalance(test_contract.address)).to.be.equal(0)
            //transfer ether to test_contract will be failed
            let transactionRequest = {
    
    
                to:test_contract.address,
                value:ethers.utils.parseEther("1.0")
            }
            await expect(owner.sendTransaction(transactionRequest)).to.be.reverted
            expect(await ethers.provider.getBalance(test_contract.address)).to.be.equal(0)
            // selfdestruct
            await expect(instance.selfdestructTest(test_contract.address)).to.be.emit(instance,"SelfDestruct")
                .withArgs(test_contract.address);
           
            expect(await ethers.provider.getBalance(instance.address)).to.be.equal(0)
            expect(await ethers.provider.getBalance(test_contract.address)).to.be.equal(ethers.utils.parseEther("1.0"))
            await expect(instance.balanceTest()).to.be.reverted
        });

        it("code and code hash test", async () => {
    
    
            let code = await instance.codeTest();
            expect(await instance.codehashTest()).to.be.equal(ethers.utils.keccak256(code));
        })

        it("Send ether success", async () => {
    
    
            // 1 send ether
            await instance.msgValueTest({
    
    
                value:ethers.utils.parseEther("1.0")
            });
            expect(await instance.balanceTest()).to.be.equal(ethers.utils.parseEther("1.0"));
            await expect(instance.sendTest(user.address,ethers.utils.parseEther("0.6"))).to.
                emit(instance,"SendEther").withArgs(user.address,ethers.utils.parseEther("0.6"),true)
        })

        it("Send ether failed", async () => {
    
    
            await instance.msgValueTest({
    
    
                value:ethers.utils.parseEther("1.0")
            });
            expect(await instance.balanceTest()).to.be.equal(ethers.utils.parseEther("1.0"));
            await expect(instance.sendTest(test_contract.address,ethers.utils.parseEther("0.6"))).to.
                emit(instance,"SendEther").withArgs(test_contract.address,ethers.utils.parseEther("0.6"),false)
        })

        it("transfer ether success", async () => {
    
    
            // 1 send ether
            await instance.msgValueTest({
    
    
                value:ethers.utils.parseEther("1.0")
            });
            expect(await instance.balanceTest()).to.be.equal(ethers.utils.parseEther("1.0"));
            await expect(instance.transferTest(user.address,ethers.utils.parseEther("0.6"))).to.
                emit(instance,"SendEther").withArgs(user.address,ethers.utils.parseEther("0.6"),true)
        })

        it("transfer ether failed", async () => {
    
    
            await instance.msgValueTest({
    
    
                value:ethers.utils.parseEther("1.0")
            });
            expect(await instance.balanceTest()).to.be.equal(ethers.utils.parseEther("1.0"));
            await expect(instance.transferTest(test_contract.address,ethers.utils.parseEther("0.6"))).to.
                be.revertedWith("function selector was not recognized and there's no fallback nor receive function")
        })

        it("contractName test", async () => {
    
    
            expect(await instance.contractNameTest()).to.be.equal("GlobalVariables")
        })

        it("creationCode and runtimeCode Test", async () => {
    
    
            let crate_code = await instance.creationCodeTest()
            assert.equal(crate_code,jsonData.bytecode)
            let run_code = await instance.runtimeCodeTest()
            assert.equal(run_code,jsonData.deployedBytecode)
        });

        it("interfaceId test", async () => {
    
    
            expect(await instance.interfaceIdTest()).to.be.equal("0x80ac58cd")
        });

        it("min test", async () => {
    
    
            expect(await instance.minTest()).to.be.equal(ethers.constants.MinInt256)
        });

        it("max test", async () => {
    
    
            expect(await instance.maxTest()).to.be.equal(ethers.constants.MaxUint256)
        });
    });
    
});

有兴趣的小伙伴可以认真看一下单元测试内容。

单元测试结果

运行npx hardhat coverage ,得到类似如下输出:

Version
=======
> solidity-coverage: v0.7.20

Instrumenting for coverage...
=============================

> GlobalVariables.sol

Compilation:
============

Compiled 1 Solidity file successfully

Network Info
============
> HardhatEVM: v2.9.1
> network:    hardhat



  GlobalVariables Test 01
    Encode and Decode test
      ✔ Encode test (50ms)
      ✔ Decode test (44ms)
      ✔ EncodePacked test
      ✔ EncodeWithSelector test (39ms)
      ✔ EncodeWithSignature test (38ms)
.......
    Crypto test
      ✔ keccak256 test
      ✔ sha256 test
      ✔ ripemd160 test
      ✔ ecrecover test
    math operation test
      ✔ add mod test
      ✔ mul mod test
    Inheritance test
      ✔ this test
      ✔ thisCall test
      ✔ super test

  GlobalVariables Test 01
    Contract test
      ✔ selfdestruct and balance test (52ms)
      ✔ code and code hash test (98ms)
      ✔ Send ether success
      ✔ Send ether failed
      ✔ transfer ether success
      ✔ transfer ether failed
      ✔ contractName test
      ✔ creationCode and runtimeCode Test
      ✔ interfaceId test
      ✔ min test
      ✔ max test


  45 passing (2s)

----------------------|----------|----------|----------|----------|----------------|
File                  |  % Stmts | % Branch |  % Funcs |  % Lines |Uncovered Lines |
----------------------|----------|----------|----------|----------|----------------|
 contracts/           |      100 |      100 |      100 |      100 |                |
  GlobalVariables.sol |      100 |      100 |      100 |      100 |                |
----------------------|----------|----------|----------|----------|----------------|
All files             |      100 |      100 |      100 |      100 |                |
----------------------|----------|----------|----------|----------|----------------|

> Istanbul reports written to ./coverage/ and ./coverage.json

猜你喜欢

转载自blog.csdn.net/weixin_39430411/article/details/123632152