溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

Hardhat怎么進(jìn)行合約測(cè)試環(huán)境準(zhǔn)備

發(fā)布時(shí)間:2023-03-09 10:18:18 來(lái)源:億速云 閱讀:101 作者:iii 欄目:開(kāi)發(fā)技術(shù)

這篇文章主要介紹了Hardhat怎么進(jìn)行合約測(cè)試環(huán)境準(zhǔn)備的相關(guān)知識(shí),內(nèi)容詳細(xì)易懂,操作簡(jiǎn)單快捷,具有一定借鑒價(jià)值,相信大家閱讀完這篇Hardhat怎么進(jìn)行合約測(cè)試環(huán)境準(zhǔn)備文章都會(huì)有所收獲,下面我們一起來(lái)看看吧。

引言

Hardhat是一個(gè)開(kāi)源的以太坊開(kāi)發(fā)框架,簡(jiǎn)單好用、可擴(kuò)展、可定制的特點(diǎn)讓它在開(kāi)發(fā)者中間很受歡迎。Hardhat在支持編輯、編譯、調(diào)試和部署合約方面都非常的方便,也有很多功能可以使合約測(cè)試工作更加高效和便捷。

一、環(huán)境準(zhǔn)備

可以參考Hardhat官網(wǎng)教程,進(jìn)行環(huán)境的準(zhǔn)備和Hardhat安裝。

Hardhat提供了快速構(gòu)建合約工程的方法:

  • 建立空的工程目錄

  • 在目錄下執(zhí)行npx hardhat

  • 根據(jù)交互提示完成Hardhat工程的創(chuàng)建

Hardhat怎么進(jìn)行合約測(cè)試環(huán)境準(zhǔn)備

二、示例合約與測(cè)試方法

快速創(chuàng)建Hardhat工程,可以在contract目錄下看到Lock.sol的合約,此合約是一個(gè)簡(jiǎn)單的示例,實(shí)現(xiàn)了在指定時(shí)間前(unlockTime)鎖定資產(chǎn)的功能。

// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.9;
// Uncomment this line to use console.log
import "hardhat/console.sol";
contract Lock {
    uint public unlockTime;
    address payable public owner;
    event Withdrawal(uint amount, uint when);
    constructor(uint _unlockTime) payable {
        require(
            block.timestamp < _unlockTime,
            "Unlock time should be in the future"
        );
        unlockTime = _unlockTime;
        owner = payable(msg.sender);
    }
    function withdraw() public {
        // Uncomment this line, and the import of "hardhat/console.sol", to print a log in your terminal
        console.log("Unlock time is %o and block timestamp is %o", unlockTime, block.timestamp);
        require(block.timestamp >= unlockTime, "You can't withdraw yet");
        require(msg.sender == owner, "You aren't the owner");
        emit Withdrawal(address(this).balance, block.timestamp);
        owner.transfer(address(this).balance);
    }
}

同時(shí),在test目錄下,有Lock.ts(或Lock.js)的測(cè)試代碼,測(cè)試代碼里分別展示了對(duì)合約部署,合約中涉及的功能的測(cè)試。其中值得學(xué)習(xí)的部分:

一是定義了一個(gè)具有setup功能的函數(shù),此函數(shù)定義了一些狀態(tài)變量的初始狀態(tài),后面在每次測(cè)試代碼運(yùn)行前,可以通過(guò)loadFixture方法執(zhí)行此函數(shù),把狀態(tài)變量還原到函數(shù)中定義的初始狀態(tài)。這種給狀態(tài)變量取快照,并用快照還原的方式,解決了很多因?yàn)闋顟B(tài)變量改變而測(cè)試用例執(zhí)行異常的問(wèn)題,是個(gè)很有用很便捷的方法。

另一個(gè)是用到了很便捷的斷言方式,這就省掉了寫(xiě)很多麻煩的校驗(yàn)條件,來(lái)驗(yàn)證一個(gè)執(zhí)行結(jié)果。比如下面這個(gè)斷言,直接能驗(yàn)證當(dāng)withdraw函數(shù)被調(diào)用后出現(xiàn)的回滾情況:

await expect(lock.withdraw()).to.be.revertedWith( "You can't withdraw yet" );

三、LoadFixture的使用

使用場(chǎng)景

用于每次執(zhí)行測(cè)試前的setup操作,可以定義一個(gè)函數(shù),在此函數(shù)中完成諸如合約部署,合約初始化,賬戶初始化等操作,在每次執(zhí)行測(cè)試前利用loadFixture的功能,進(jìn)行相同的變量狀態(tài)的設(shè)置,對(duì)合約測(cè)試提供了很大的幫助。

工作原理

根據(jù)Hardhat源碼,可以看到loadFixture維護(hù)了一個(gè)快照數(shù)組snapshots,一個(gè)快照元素包含:

不同的函數(shù)f作為loadFixture入?yún)r(shí),會(huì)有不同的snapshot存儲(chǔ)在loadFixture維護(hù)的snapshots數(shù)組中。

在loadFixture(f)首次執(zhí)行時(shí),屬于f函數(shù)的snapshot為undefined,此時(shí)會(huì)記錄f函數(shù)中定義的全部狀態(tài)變量,同時(shí)執(zhí)行:

const restorer = await takeSnapshot();

并將此時(shí)的snapshot元素加入到snapshots數(shù)組中,后面再次用到同一個(gè)入?yún)⒑瘮?shù)f的loadFixture時(shí),在快照數(shù)組snapshots中已存在快照,可直接進(jìn)行區(qū)塊鏈狀態(tài)回滾: await snapshot.restorer.restore();

  • fixture: Fixture類型的入?yún)⒑瘮?shù),type Fixture = () => Promise;

  • data:fixture函數(shù)中定義的狀態(tài)變量

  • restorer:一個(gè)有restore方法的結(jié)構(gòu)體,在“./helpers/takeSnapshot”方法中有定義,可以觸發(fā)evm_revert操作,指定區(qū)塊鏈退回到某個(gè)快照點(diǎn)。

loadFixture的用法

官方文檔示例如下:

```js
async function deployContractsFixture() {
  const token = await Token.deploy(...);
  const exchange = await Exchange.deploy(...);
  return { token, exchange };
}
it("test", async function () {
  const { token, exchange } = await loadFixture(deployContractsFixture);
  // use token and exchanges contracts
})
```
注意:loadFixture的入?yún)⒉豢梢允悄涿瘮?shù),即:
```js
//錯(cuò)誤寫(xiě)法 
loadFixture(async () => { ... }) 
//正確寫(xiě)法 
async function beforeTest(){ 
//定義函數(shù) 
} 
loadFixture(beforeTest);
```

四、Matchers的使用

Machers:在chai斷言庫(kù)的基礎(chǔ)上增加了以太坊特色的斷言,便于測(cè)試使用

1.Events用法

   contract Ademo {
    event Event();
    function callFunction () public {
        emit Event();
    }
}

對(duì)合約C的call方法進(jìn)行調(diào)用會(huì)觸發(fā)一個(gè)無(wú)參數(shù)事件,為了測(cè)試這個(gè)事件是否被觸發(fā),可以直接用hardhat-chai-matchers中的Events斷言,用法如下:

    const A=await ethers.getContractFactory("Ademo");
    const a=await A.deploy();
    //采用hardhat-chai-matchers的斷言方式,判斷Events是否觸發(fā)
    await expect(a.callFunction()).to.emit(a,"Event");
  • Reverts用法:

    //最簡(jiǎn)單的判斷revert的方式
await expect(contract.call()).to.be.reverted;
//判斷未發(fā)生revert
await expect(contract.call()).not.to.be.reverted;
//判斷revert發(fā)生并且?guī)Я酥付ǖ腻e(cuò)誤信息
await expect(contract.call()).to.be.revertedWith("Some revert message");
//判斷未發(fā)生revert并且攜帶指定信息
await expect(contract.call()).not.to.be.revertedWith("Another revert message");

除了上述常用的判斷場(chǎng)景外,hardhat-chai-matchers還支持了對(duì)Panic以及定制化Error的判定:

await expect(…).to.be.revertedWithPanic(PANIC_CODES)
await expect(…).not.to.be.revertedWithPanic(PANIC_CODES)
await expect(…).to.be.revertedWithCustomError(CONTRACT,"CustomErrorName")
await expect(…).to.be.revertedWithoutReason();
  • Big Number

在solidity中最大整型數(shù)是2^256,而JavaScript中的最大安全數(shù)是2^53-1,如果用JS寫(xiě)solidity合約中返回的大數(shù)的斷言,就會(huì)出現(xiàn)問(wèn)題。hardhat-chai-matchers提供了關(guān)于大數(shù)的斷言能力,使用者無(wú)需關(guān)心大數(shù)之間比較的關(guān)系,直接以數(shù)字的形式使用即可,比如: expect(await token.balanceOf(someAddress)).to.equal(1);

關(guān)于JavaScript的最大安全數(shù)問(wèn)題:

Number.MAX_SAFE_INTEGER 常量表示在 JavaScript 中最大的安全整數(shù),其值為2^53-1,即9007199254740991 。因?yàn)镴avascript的數(shù)字存儲(chǔ)使用了IEEE 754中規(guī)定的雙精度浮點(diǎn)數(shù)數(shù)據(jù)類型,而這一數(shù)據(jù)類型能夠安全存儲(chǔ)(-2^53-1 ~ 2^53-1)之間的數(shù)(包括邊界值),超出范圍后將會(huì)出現(xiàn)錯(cuò)誤,比如:

const x = Number.MAX_SAFE_INTEGER + 1;
const y = Number.MAX_SAFE_INTEGER + 2;
console.log(Number.MAX_SAFE_INTEGER);
// Expected output: 9007199254740991
console.log(x);
console.log(y);
// Expected output: 9007199254740992
console.log(x === y);
// Expected output: true

Balance Changes

可以很方便的檢測(cè)用戶錢(qián)包的資金變化額度,適用于以太幣的金額變化,或者ERC-20代幣的金額變化。

單個(gè)錢(qián)包地址的金額變化:

await expect(() =&gt;
  sender.sendTransaction({ to: someAddress, value: 200 })
).to.changeEtherBalance(sender, "-200");
await expect(token.transfer(account, 1)).to.changeTokenBalance(
  token,
  account,
  1
);

也可以用來(lái)檢測(cè)多個(gè)賬戶的金額變化,在測(cè)試轉(zhuǎn)賬交易時(shí),非常適用:

await expect(() =>
  sender.sendTransaction({ to: receiver, value: 200 })
).to.changeEtherBalances([sender, receiver], [-200, 200]);
await expect(token.transferFrom(sender, receiver, 1)).to.changeTokenBalances(
  token,
  [sender, receiver],
  [-1, 1]
);
  • 字符串比較

可以用hardhat-chai-matchers提供的方法,方便地校驗(yàn)各種復(fù)雜的字符串,比如一個(gè)字符串是否是正確的地址格式、私鑰格式等,用法如下:

// 是否符合address格式
expect("0xAb8483F64d9C6d1EcF9b849Ae677dD3315835cb2").to.be.a.properAddress;
//是否符合私鑰格式
expect(SOME_PRI_KEY).to.be.a.properPrivateKey;
//判斷十六進(jìn)制字符串的用法
expect("0x00012AB").to.hexEqual("0x12ab");
//判斷十六進(jìn)制字符串的長(zhǎng)度
expect("0x123456").to.be.properHex(6);

關(guān)于“Hardhat怎么進(jìn)行合約測(cè)試環(huán)境準(zhǔn)備”這篇文章的內(nèi)容就介紹到這里,感謝各位的閱讀!相信大家對(duì)“Hardhat怎么進(jìn)行合約測(cè)試環(huán)境準(zhǔn)備”知識(shí)都有一定的了解,大家如果還想學(xué)習(xí)更多知識(shí),歡迎關(guān)注億速云行業(yè)資訊頻道。

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI