等待/偵聽在測試中觸發的外部事件 - ethers / hardhat
我正在等待一個事件在我的測試中觸發。這不是從我正在執行的交易中發生的,所以我不能使用交易收據中的任何東西(例如,這些不會在安全帽測試網路上使用 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()
,那麼你不能使用完成。它必須是其中之一。這很好,因為無論如何這都會搞砸。此外, using
done
是舊的做事方式,所以我們希望盡可能使用 Promise。如果我們只是使用
()
或者(done)
我們將不得不使用.then
and.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
.
- try/catch 是怎麼回事?
所以我們的回調函式有
assert
我們測試所需的,為什麼它在 try/catch 中?我們的assert
函式在失敗時實際上會拋出一個錯誤,所以如果它在我們的回調函式中失敗,我們將永遠無法呼叫resolve
或reject
讓 Promise 知道我們的測試已經完成。這意味著您將遇到一個非常醜陋的UnhandledPromiseRejectionWarning
錯誤(是的,這是一個錯誤 - 即使名稱中顯示的是警告)並且您將永遠不會離開回調,因此您最終會遇到超時或永遠卡在那裡.這就是我們將它包裝在 try catch 中的原因。如果斷言通過,是的,我們呼叫
resolve
. 如果沒有,好的,我們將失敗的原因傳遞e
給我們的catch
和呼叫reject(e)
。斷言總是通過
如果你不這樣包裝你的測試,無論如何你也可能會遇到你的
asserts
pass問題。真正發生的是,它們可能從未真正被擊中,因此您需要確保當它們在回調函式中時將它們包裝在 try catch 中。