Truffle-編寫精準時間依賴測試

買賣虛擬貨幣
當你在以太坊上搭建一個工資型別的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)中確定性時間跳躍的歷史。

免責聲明:

  1. 本文版權歸原作者所有,僅代表作者本人觀點,不代表鏈報觀點或立場。
  2. 如發現文章、圖片等侵權行爲,侵權責任將由作者本人承擔。
  3. 鏈報僅提供相關項目信息,不構成任何投資建議

推荐阅读

;