DApp:開發一條龍,測試一條蟲?

買賣虛擬貨幣

來源 | 《區塊鏈開發實戰:基於JavaScript的公鏈與DApp開發》

責編 | Carol

出品 | 區塊鏈大本營(blockchain_camp)

#聽說文末有彩蛋?

2017年,這一年有點特別,許多先進的技術和新的概念集中在這一年迸發。小程式火了、新零售火了、區塊鏈火了、人工智慧火了、物聯網也火了。

它們有的是首次面世,也有的是早已默默發展了很久,等待一個契機走向大眾。

而這當中出現過一個讓人印象深刻的小插曲:

這個回答曾經在網路上紅極一時,在開懷大笑的同時也不禁發人深思:新的技術若無法落到具體的應用場景解決問題,終究還是紙上談兵。

區塊鏈技術更是如此。雖說目前區塊鏈的一些技術瓶頸還有待突破,但開發者們應該積極去探索、多嘗試,尤其是在應用方面。其中開發DApp就是一個很好的選擇。

不得不提的是,在開發DApp時,大部分開發者都會把重心放在開發的過程中,但實際上,還有同樣值得開發者們注意的重要一環:測試

接下來,我們就以抽獎合約為例,抽獎合約的整個測試流程程式碼來講解如何對合約與介面進行測試。

做好準備,又要開始我們的乾貨時間了。

測試準備

首先我們來看一下專案中,test 目錄中檔案結構,lib 目錄中存放了方便測試呼叫的封裝函式,以 base 命名,而 test 根目錄的 cctime 檔案包含了主要的測試用例。

編寫測試用例之前,我們先熟悉一下 base 檔案中的函式,這些函式作為測試工具提供給測試用例呼叫,封裝了合約和訪問介面程式碼。

1、初始化函式

編寫測試用例之前,需要將常用的方法抽離封裝,放入base 檔案中,這裡我們使用了 supertest 和 chai 作為主要的測試框架,大家可以在原始碼檔案中找到測試檔案中的宣告。以下是初始化相關的函式:

我們看 init 方法中,對 DApp 的 id 進行了查詢,根據應用的名稱從主鏈動態獲取當前側鏈應用的 ID ,為後續測試介面的呼叫初始化 DappId 資料。

接下來我們看一下測試常用的工具函式。

2、區塊等待

在發起一筆交易之後,需要等待交易確認之後再執行下一步的操作,呼叫 sleep函式進行等待,之後繼續執行。這個函式在測試流程中會多次使用,因為 10 秒一個區塊的特性,很多的操作需要在區塊確認之後獲得驗證,不僅是寫操作,讀取的介面依然需要在上一次寫操作之後等待區塊確認才能獲取到最新資料。區塊等待相關函式如下所示:

3、賬戶生成與轉賬

生成隨機賬戶與轉賬介面也需要測試,我們留意到了在 base 檔案頭部定義了創世賬戶的地址和秘鑰,創世賬戶可以透過 asch-js 中的合約介面向新生成的賬戶轉賬,隨機賬戶有了餘額就能夠繼續呼叫應用中的自定義合約,進行合約相關的功能測試。賬戶及轉賬相關的函式如下:

a. 隨機賬戶

randomSecret 呼叫 randomSecret 生成隨機字串作為賬戶秘鑰,我們可以看到AschJS.crypto.getKeys 函式能夠將字串格式的秘鑰透過非對稱加密得出一個包含公鑰和私鑰的秘鑰對,AschJS.crypto.getAddress 透過公鑰算出賬戶的地址。

randomSecret 返回的是一個隨機生成但被擷取之後的字串。通常情況下,Asch 只支援符合 BIP39 規範的金鑰字元,也就是我們熟悉的“助記詞”格式的密碼,但這裡為了測試方便,直接使用隨機的七位字串,同樣可以算出符合規則的公鑰,也能計算出地址。當然,隨機賬戶也支援透過指定助記詞的方式獲取公鑰與地址。

b. 轉賬

轉賬在 DApp 以型別 2 的合約實現,所以這裡的轉賬就是在呼叫 DApp 內部的合約,我們可以在 giveMoney 函式中看到合約呼叫的格式。

合約引數結構如下:

  • secret為合約呼叫者的秘鑰,String 型別。

  • fee為合約呼叫手續費,bigNumber型別。

  • type為合約型別,Number 型別,與自定義合約資料對應。

  • args為合約引數,Array型別。

意:

我們看到 giveMoney 呼叫合約時請求了 /transactions/unsigned 介面,這個介面可以接受未簽名的引數和金鑰執行合約,這樣做在測試環境雖然沒有問題,但是在正式的生產環境中會有很大的風險,我們的私鑰內容會有被網路劫持的風險,所以在呼叫合約時,儘可能避免透過網路傳輸自己的金鑰,而是用本地簽名的方式加密引數,然後請求 /transactions/signed ,這點一定要十分注意。

上面的程式碼透過接收簽名引數呼叫合約的介面,這個函式傳送了命名為 transaction 的引數,trs 是用 asch-js 前端 JavaScript 工具庫進行簽名返回的 transaction 物件。我們來看一個例子:

使用 AschJS.dapp.createInnerTransaction 將合約引數透過秘鑰 secret 簽名之後傳入 submitInnerTransaction 函式,完成合約呼叫。與上面 giveMoney 函式不同的是,createInnerTransaction 返回的是透過秘鑰簽名的內容,將簽名後的資料透過網路傳送,這樣提高了整個傳輸過程秘鑰的安全性。

我們來看簽名後的 transaction 引數是什麼樣子:

與上面未簽名呼叫轉賬介面的引數對比,本地簽名後得出的引數中少了 secret 屬性,多了 signature 屬性,而這個屬性把透過 sha256 演算法得出的私鑰與整個 transaction 引數經過雜湊計算之後得出,用於後端介面對引數驗證。

其他的合約呼叫基本上都按照發布文章合約的結構組織引數,完成合約呼叫的封裝。這樣,我們就可以著手編寫測試了。

合約流程測試

我們現在開始以一個釋出文章、使用者打賞、結算抽獎和使用者領獎整個應用的核心流程進行測試,相關程式碼如下:

首先,在測試用例 before 函式中初始化測試變數、DApp 資料和創始賬戶資訊作為後續測試函式的基礎,然後執行獲取頻道列表的測試用例。我們使用 await base.dappApiGetAsync('/channels') 請求一個 API,獲取到頻道列表資訊,並用斷言庫校驗結果。

下面我們對核心的業務流程進行測試,測試的思路如下:

1)建立頻道。

2)初始化賬戶。

3)在頻道里建立包含抽獎模式的文章。

4)模擬三個使用者各打賞兩筆。

5)文章結算。

6)獲獎使用者領獎。

7)檢查各自賬戶的餘額。

核心業務流程的程式碼如下:

上面的程式碼使用受託人建立了一個新頻道,並透過頻道查詢介面透過交易 ID 獲取到了頻道的 ID,完成了基本的測試邏輯,同時儲存了 channelId 作為後續建立文章的資料。

注意:

await base.onNewBlockAsync() 是在等待區塊確認之後再繼續執行。我們看到最初先給賬戶轉入 10500 的 Token,用於建立頻道和更新頻道的消耗。

打賞文章測試程式碼如下:

在上面程式碼中,首先進行賬戶的初始化,生成了四個賬戶,一個賬戶負責建立文章和結算獎勵,另外三個作為打賞使用者。然後對建立的文章執行兩次打賞,為了驗證方便,新建立的賬戶兩次打賞的總額為5個Token,建立文章的賬戶擁有 0.2 個Token,操作之後扣掉手續費保證在結算之前賬戶餘額都是零,方便驗證。

另外,我們為了測試需要,將後端關於區塊高度的限制暫時去掉,並設定結算區塊高度為當前的高度加 2,這樣,在使用者投票之後直接執行結算。

提示:

測試程式碼中,建立頻道或文章之後,因為需要區塊確認,所以我們沒有辦法立即獲取到資料的 ID,只能先拿到 transactionId,待區塊打包之後,再透過查詢介面用 tid 獲取實際的資料 ID,再進行下一步的操作,測試檔案中,會出現很多這樣的處理,這也是區塊特性所決定的。

使用者打賞測試程式碼如下:

上面程式碼中,用另外三個賬戶對文章進行了打賞,每個賬戶打賞兩次不同的金額,但總額是 5 XCT,所以最終文章的抽獎池中,應該是 15 個 XCT,結算結果根據 15 XCT 的總額進行驗證,然後驗證文章投票額與投票者的餘額是否正確,程式碼如下:

合約中對抽獎模式的結算規則是受託人 10%,作者 30%,獲獎者60%,因為受託人的獎勵是平均分給三個賬戶,所以驗證不是那麼方便,不過我們只要驗證作者和獲獎者的獎勵額就能確定結算是否正確,那麼最終的結果是作者獲得 4.5 個 XCT,抽獎人獲得 9 個 XCT。

驗證獎勵測試程式碼如下:

最終,在根目錄執行 npm test ,等測試執行結束,就能看到應用測試執行的結果了。

總結

上述提及的測試程式碼也只是完成了核心功能驗證,並沒有完全覆蓋到每一個合約和操作場景,如果讀者感興趣可以嘗試在此基礎上補充或重構,也歡迎對專案提出改進建議。

免責聲明:

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

推荐阅读

;