Events

等待/偵聽在測試中觸發的外部事件 - ethers / hardhat

  • February 5, 2022

我正在等待一個事件在我的測試中觸發。這不是從我正在執行的交易中發生的,所以我不能使用交易收據中的任何東西(例如,這些不會在安全帽測試網路上使用 ethers.js 監聽事件)。我認為這個答案只適用於松露。

這是我的一項測試:

it.only("Should successfully make an external API request and get a result", async (done) => {
     apiConsumer.once("DataFullfilled", () => {
       const result = await apiConsumer.volume()
       assert(result > 0)
       done()
     })
     apiConsumer.requestVolumeData()
})

我嘗試了許多變化,但我似乎無法弄清楚我做錯了什麼。這目前給了我Error: Resolution method is overspecified. Specify a callback *or* return a Promise; not both.我已經閱讀了有關此錯誤的mocha 文件和其他一些文件,但我似乎無法弄清楚發生了什麼。

作為參考,您可以在此處查看我的完整程式碼。您可以通過執行npx hardhat test --network rinkeby並確保您擁有測試網 ETH 和 LINK 來執行測試。

您必須從第一行中刪除 async 關鍵字,並將其移至apiConsumer.once.

將其從 中刪除的原因it是向函式添加 async 會使其返回一個 Promise,這就是 mocha 所抱怨的(使用 async 或 done 回調參數,但不能同時使用兩者)。

而且您需要在內部回調中使用非同步,因為您在其主體中使用了等待。

在聖地亞哥的幫助下,我終於搞定了!所以程式碼應該是這樣的:

it.only("Our event should successfully fire on callback", async () => {
     const callbackValue = 777

     // we setup a promise so we can wait for our callback from the `once` function
     await new Promise(async (resolve, reject) => {
       // setup listener for our event
       apiConsumer.once("DataFullfilled", async () => {
         console.log("DataFullfilled event fired!")
         const volume = await apiConsumer.volume()
         // assert throws an error if it fails, so we need to wrap
         // it in a try/catch so that the promise returns event
         // if it fails.
         try {
           assert.equal(volume.toString(), callbackValue.toString())
           resolve()
         } catch (e) {
           reject(e)
         }
       })
       const transaction = await apiConsumer.requestVolumeData()
       const transactionReceipt = await transaction.wait(1)
       const requestId = transactionReceipt.events[0].topics[1]
       await mockOracle.fulfillOracleRequest(requestId, numToBytes32(callbackValue))
     })
   })

這就是為什麼。

1.如何使用回調it

it.only("Our event should successfully fire on callback", async () => {

當你決定創建你的回調函式async(通過它async ())時,你是在告訴 mocha 你將返回一個包含測試結果的 Promise。您可以 100% 使用看起來像(done)但無法返回承諾的回調。如果你使用async(),那麼你不能使用完成。它必須是其中之一。這很好,因為無論如何這都會搞砸。

此外, usingdone舊的做事方式,所以我們希望盡可能使用 Promise。

如果我們只是使用()或者(done)我們將不得不使用.thenand.catch語法……在我看來這有點糟糕。

2.如何確保在我們的函式中等待回調once函式。

為了讓一切都等待(包括 mocha 本身),我們將整個 shebang 包裝成一個Promise. 我們為什麼要這樣做?好吧,我們可以強制我們的測試來await完成整個事情。猜猜看,我們可以在這個 Promise 中描述我們希望我們的測試做的所有事情!

我們需要這樣做,因為我們的成功標準是在回調函式中定義的once。應該ethers更新,以便我們可以做一個await contract.event("EVENT_NAME")也許應該在回購中提出的問題……但目前,他們沒有。

現在,當我們希望 promise 成功完成時,我們呼叫resolve並且如果我們希望它為我們的測試拋出錯誤,我們呼叫reject,我們的 promise 中。

3.如何設置它以便我們監聽事件觸發

你會注意到,我們在開始呼叫事務apiConsumer.once *之前呼叫。*這是有道理的。我們想將我們的監聽器添加到事件循環中。這意味著,在後台,我們有這段程式碼一直在等待觸發該事件。

所以我們將它設置為監聽,然後我們稍後觸發事件await mockOracle.fulfillOracleRequest(requestId, numToBytes32(callbackValue))(它有一些程式碼來觸發事件)然後它會呼叫我們的非同步回調函式(描述為我們的apiConsumer.once.

  1. try/catch 是怎麼回事?

所以我們的回調函式有assert我們測試所需的,為什麼它在 try/catch 中?我們的assert函式在失敗時實際上會拋出一個錯誤,所以如果它在我們的回調函式中失敗,我們將永遠無法呼叫resolvereject讓 Promise 知道我們的測試已經完成。這意味著您將遇到一個非常醜陋的UnhandledPromiseRejectionWarning錯誤(是的,這是一個錯誤 - 即使名稱中顯示的是警告)並且您將永遠不會離開回調,因此您最終會遇到超時或永遠卡在那裡.

這就是我們將它包裝在 try catch 中的原因。如果斷言通過,是的,我們呼叫resolve. 如果沒有,好的,我們將失敗的原因傳遞e給我們的catch和呼叫reject(e)

斷言總是通過

如果你這樣包裝你的測試,無論如何你也可能會遇到你的assertspass問題。真正發生的是,它們可能從未真正被擊中,因此您需要確保當它們在回調函式中時將它們包裝在 try catch 中。

引用自:https://ethereum.stackexchange.com/questions/120855