Truffle-編寫精準時間依賴測試
By 區塊鏈研究實驗室·
當你在以太坊上搭建一個工資型別的Dapp時,編寫精確的時間依賴性測試是必要的。在開始之前,你需要搭建的環境如下:· 您的測試框架是Truffle、Ganache+Mocha。· 測試儘可能精確(<5秒誤差範圍)。在為以太坊智慧合約編寫的測試中操縱時間會帶來一些問題。陷阱#1:RPC雖然在Ganache中完全可以把時間向前和向後跳躍,但有多種方法可以實現這一目標。要獲得準確的結果,必須按以下方式使用evm_mine RPC方法:{ "jsonrpc": "2.0", "method": "evm_mine", "params": ["NUMBER_OF_SECONDS"], "id": 1}advanceBlockAtTime.json也作為javascript的許可:const advanceBlockAtTime = (time) => { return new Promise((resolve, reject) => { web3.currentProvider.send( { jsonrpc: "2.0", method: "evm_mine", params: [time], id: new Date().getTime(), }, (err, _) => { if (err) { return reject(err); } const newBlockHash = web3.eth.getBlock("latest").hash; return resolve(newBlockHash); }, ); });};advanceBlockAtTime.js 筆記:· 如果沒有NUMBER_OF_SECONDS引數,則RPC呼叫僅增加塊高度,但不會及時跳轉。· “id”引數是可選的,但很好。你在測試時把什麼值放進去並不重要。還有evm_increaseTime,它增加了Ganache的“內部時鐘”,這樣無論何時挖掘下一個區塊,它都有一個時間戳偏移量。這增加了開銷:// Not what you wantadvanceTimeAndBlock = async (time) => { await advanceTime(time) await advanceBlock() return Promise.resolve(web3.eth.getBlock('latest'))}advanceTimeAndBlock.js 沒錯,你必須進行兩次RPC呼叫,而第一種方法只需要一次。Jakub Wojciechowski提出道具,為ganache-core提供PR#13。如果沒有確定性和原子性的方法在Ganache中及時跳躍,那麼編寫準確的測試將會很困難。陷阱#2:執行時間(run time)程式碼本身需要時間來執行。具體來說,Javascript許可需要花費不可忽略的時間來解決問題。這很明顯,對吧?當為以太坊智慧合約編寫依賴時間的測試時,事情變得很微妙。考慮以下內容:1.測試用例執行所需的時間2.您希望將來跳轉的秒數3.在控制檯中提交yarn run test時的unix時間戳取決於這些變數,您的測試塊可能會在一秒或多秒的過程中被捕獲,因此您的斷言可能會中斷。例如:describe("when the stream did start but not end", function() { beforeEach(async function() { await advanceBlockAtTime( now .plus(STANDARD_TIME_OFFSET) .plus(5) .toNumber(), ); }); describe("when the withdrawal amount is within the available balance", function() { const amount = new BigNumber(5).multipliedBy(1e18).toString(10); it("makes the withdrawal", async function() { const balance = await this.token.balanceOf(recipient); await this.sablier.withdraw(streamId, amount, opts); const newBalance = await this.token.balanceOf(recipient); balance.should.be.bignumber.equal(newBalance.minus(amount)); }); });});makeWithdrawal.js 這樣做是因為它要求Sablier合同撤回以前存入的餘額。ERC-1620中規定呼叫者可以退出多少的規則。我在區塊之前記錄了mocha的unix時間戳,我測量了使用節點的效能時序api執行測試所需的時間:t0 1565455128964Call to sablier.withdraw took 115.92673601210117 milliseconds. 1) makes the withdrawalsablierWithdrawTest.txt 如果您將115加上156545128964,則最終得到一個以9079結尾的數字,因此秒數從8增加到9。這就是打破這個斷言的原因,因為我期望X的餘額,當我實際得到X+1時。(超過秒數=更多錢)雖然編寫一個可以計算另一個程式P2完成所需時間的程式P1是不可能的(參見圖靈的暫停問題),但我們可以安全地假設你的beforeEach和它之間的阻塞時間不應超過1秒。這假設您的節點例項和ganache之間的來回通訊幾乎是即時的,即使在執行覆蓋時也是如此。這是修復:balance.should.bignumber.satisfy(function(num) { return ( num.isEqualTo(newBalance.minus(amount)) || num.isEqualTo(newBalance.minus(amount).plus(ONE_UNIT)) );});makeWithdrawalFix.js 其中ONE_UNIT是每秒分配的一個貨幣單位,根據Sablier模型。它不完美,但比使用“大於”或“小於”平等檢查更好。最後,正如OpenZeppelin團隊在此論證的那樣,您可能不需要這種精確度。如果你的dapp不直接涉及時間戳或區塊數,那麼容忍更大的時間偏移是完全正常的。陷阱#3:BeforeEach和AfterEach您的里程可能會有所不同,但您可能希望在“beforeEach”中向前跳躍並在“afterEach”中向後跳躍。這是因為您的合同可能在“describe”塊的範圍內定義了一些變數,並且您希望執行一系列“it”塊,這些塊都採用相同的狀態。不回覆“afterEach”只會永遠增加時間戳。例如:describe("when the stream did start but not end", function() { const amount = new BigNumber(5).multipliedBy(1e18).toString(10); beforeEach(async function() { await web3.utils.advanceBlockAtTime( now .plus(STANDARD_TIME_OFFSET) .plus(5) .toNumber(), ); }); it("test1", function() {}); it("test2", function() {}); it("test3", function() {}); afterEach(async function() { await web3.utils.advanceBlockAtTime(now.toNumber()); });});beforeAfterEach.js 正如您在上面的程式碼片段中看到的,我們有三個測試,其中我們假設提取的金額為5。在Sabilier的上下文中,在時間15秒內前進將產生15秒的可提取量,因此我們必須在“aftereach”塊中返回到原始狀態。陷阱#4:快照在完成所有測試後返回到原始狀態。它可能對CI或其他外部環境有幫助。takeSnapshot = async () => { return new Promise((resolve, reject) => { web3.currentProvider.send( { jsonrpc: "2.0", method: "evm_snapshot", id: new Date().getTime(), }, (err, snapshotId) => { if (err) { return reject(err); } return resolve(snapshotId); }, ); });};revertToSnapshot = async (id) => { return new Promise((resolve, reject) => { web3.currentProvider.send( { jsonrpc: "2.0", method: "evm_revert", params: [id], id: new Date().getTime(), }, (err, result) => { if (err) { return reject(err); } return resolve(result); }, ); });};snapshotFunctions.js在程式碼庫中定義這些函式,然後將其插入到一個根測試檔案中:let snapshot;let snapshotId;before(async () => { snapshot = await takeSnapshot(); snapshotId = snapshot.result;});after(async () => { await revertToSnapshot(snapshotId);});truffleSnapshots.js 現在你的區塊鏈將在所有神奇的時間跳躍後恢復到原來的時間戳。本文中使用的一些零碎內容受到啟發或從其他著作中獲取,例如Ethan Wessel令人驚歎的“使用truffle測試時間”。 該文章唯一的警告是使用evm_mine和evm_increaseTime,我們在上面解釋了為什麼這不理想。此外,這是一個非常好的StackExchange執行緒,它具有block.timestamp和一些GitHub執行緒的固有安全性,它們揭示了在Ganache(1和2)中確定性時間跳躍的歷史。#Truffle#Dapp#通證模型
免責聲明:
- 本文版權歸原作者所有,僅代表作者本人觀點,不代表鏈報觀點或立場。
- 如發現文章、圖片等侵權行爲,侵權責任將由作者本人承擔。
- 鏈報僅提供相關項目信息,不構成任何投資建議。
推荐阅读