相關文章:正文
1 #[cfg(not(target_arch = "wasm32"))]
2 #[cfg(test)]
3 mod tests {
4 use super::*;
5 use near_sdk::MockedBlockchain;
6 use near_sdk::{testing_env, VMContext};
7
8 ...
9}
正文"wasm32"))]。
Rust 智能合約養成日記(1)合約狀態數據定義與方法實現
在上一期BlockSec 針對Rust 智能合約開發的文章中,我們介紹瞭如何為合約StatusMessage 定義合約狀態,並為該合約實現了不同的方法。本期我們將繼續基於該合約展開敘述,詳細介紹編寫單元測試用例的方法,並在本地進行合約的測試。
1. 準備單元測試環境
1 fn get_default_context(view_call: bool) -> VMContext {
2 VMContext {
3 current_account_id: "alice_near".to_string(),
4 signer_account_id: "bob_near".to_string(),
5 signer_account_pk: vec!,
6 predecessor_account_id: "carol_near".to_string(),
7 input: vec!,
8 block_index: 0,
9 block_timestamp: 0,
10 account_balance: 0,
11 account_locked_balance: 0,
12 storage_usage: 0,
13 attached_deposit: 0,
14 prepaid_gas: 10u64.pow(18),
15 random_seed: vec!,
16 is_view: view_call,
17 output_data_receivers: vec!,
18 epoch_height: 0,
19 }
20 }
為編寫單元測試,首先我們需要在src/lib.rs 中加入如下代碼,對單元測試進行環境設置:
在上述代碼的第1-3 行中,我們為StatusMessage 添加了tests 子模塊(使用mod 關鍵字聲明該新模塊),並在該模塊的代碼片段之前標註了cfg 屬性宏#[cfg(test)] 。此外,由於Rust 的本地單元測試無需獲得Wasm 代碼,因此可為該測試模塊配置Rust 編譯條件#[cfg(not(target_arch =
代碼第4-6 行從near_sdk (NEAR 的軟件開發工具包)中導入了合約測試環境的相關依賴項。具體觀察代碼的每一行中,use 關鍵詞的用法類似於python 語言代碼在導入其他所依賴的模塊時所使用的import。 use 聲明可創建一個或多個與其他路徑同義的局部名稱綁定,即通常可使用use 關鍵詞來聲明引用模塊項所需的路徑,且這些聲明通常可能出現在Rust 模塊或代碼塊的頂部。
在第4 行中,super 關鍵字可用於從當前模塊訪問父模塊StatusMessage,使得能夠訪問父模塊中所定義的功能與方法,如之前我們為StatusMessage 合約所定義的方法函數set_status 與get_status。第5 行使用use 關鍵詞引用了nearsdk 所提供的模擬區塊鏈MockedBlockchain 支持模塊,可用於智能合約的測試。第6 行則從nearsdk 引入了合約測試執行的環境,以及有關測試環境上下文信息格式的支持。
near_sdk::env::current_account_id()
near_sdk::env::predecessor_account_id()
near_sdk::env::signer_account_pk()
near_sdk::env::input()
near_sdk::env::predecessor_account_id()
在導入支持NEAR 智能合約單元測試所需的外部依賴模塊後,我們還需要在測試模塊中定義如下函數get_context(),用於配置並返回測試環境中所需使用的上下文信息:VMContext。
VMContext 設定了多個模擬的,合約用戶賬戶信息,以及包括區塊高度,區塊時間戳,合約存儲用量等在內的區塊鏈底層相關的上下文配置信息。
下面首先對VMContext 中幾處關鍵的屬性配置加以說明:
current_account_id: 執行當前合約的帳戶。 signer_account_id: 觸發當前合約函數調用執行的交易簽名者。所有的合約調用都是某個交易的結果,且該交易由某個帳戶使用其訪問密鑰(Access Key) 簽署,該賬戶即為signer_account_id。 signer_account_pk: 交易簽名者所使用的Access Key 公鑰(Public Key)。 predecessor_account_id: 當合約的執行屬於跨合約調用或回調時,該屬性指代了該調用的發起者帳戶。而當進行單一的合約內部函數調用時,該值將與signer_account_id 一致。 prepaid_gas: 在區塊鏈中執行合約時存在一個特點,即用戶需要支付一定的交易執行費用(gas fee)。這裡的prepaid_gas 設定了可供當前交易合約函數調用時所能扣除的Gas 最大值,並附加到當前的合約調用中。 is_view: 該參數is_view(類型為bool) 可設置合約函數的調用能否對合約的狀態數據進行修改。若該值為ture,則合約函數執行時,合約的狀態數據是只讀的。反之如果該值為false,則合約的執行環境將允許對合約數據進行修改。 VMContext 中其餘屬性的內容和用法將在後續的文章中詳細展開描述。
1 #[test]
2 fn set_get_message() {
3 let context = get_default_context(false);
4 testing_env!(context);
5 let mut contract = StatusMessage::default();
6 contract.set_status("hello".to_string());
7 assert_eq!(
8 "hello".to_string(),
9 contract.get_status("bob_near".to_string()).unwrap()
10 );
11 }
當執行NEAR 合約時,程序可配合一些NEAR SDK 所提供的相關API 讀取這些已設置的上下文信息。例如:
上述API 均可返回上下文具體屬性的值,這些API 可以使用前文所述的use 聲明導入。
在定義完函數get_context() 後,我們便可以在test 模塊中逐個地編寫單元測試的內容了。
2. 單元測試一
如下是單元測試1 的代碼片段:"Hello"現在我們對測試用例的具體寫法展開描述:"assertion failed"上述代碼片段的第1 行,我們為該單元測試函數標註了#[test] 宏,表明這是該單元測試的起點。緊接著第2 行,便是該單元測試函數set_get_message() 的聲明。
代碼的3-10 行即該單元測試函數內部的主要測試邏輯,其中的代碼實現首先將調用前面所定義的get_context 初始化一個測試環境中所使用的上下文context。此外值得一提的是,由於本單元測試需要向合約的狀態數據中寫入數據,因此需要為get_context 設置參數,將前文所述VMContext 中的is_view 屬性設置為false,否則單元測試內部將引發panic 導致測試無法通過。
在設置得到一個合理的合約執行上下文後,代碼的第4 行將利用該上下文VMContext,使用testing_env! 宏初始化一個用於智能合約交互的MockedBlockchain 實例。代碼的第5 行將調用父模塊中定義的StatusMessage::default() 生成初始化後的合約對象contract。
assert_eq!(left,在後續的代碼中,測試會首先調用父模塊StatusMessage 所定義的set_status 方法,在合約狀態數據中保存字符串
assert_ne!(left,。隨後再利用get_status 從合約狀態數據中讀取該條數據,並與期望所獲得內容進行對比。如果內容相互匹配,則通過該單元測試,若不匹配則會在該測試線程中觸發
類型的panic。
有關單元測試中利用斷言assert 進行校驗的寫法描述如下:
1 #[test]
2 fn get_nonexistent_message() {
3 let context = get_default_context(true);
4 testing_env!(context);
5 let contract = StatusMessage::default();
6 assert_eq!(None, contract.get_status("francis.near".to_string()));
7 }
assert!(expression) 宏可檢驗boolean 值,當且僅當expression 表達式所指代的內容為true 時則通過檢驗;
right) 宏常用於校驗是否相等,當且僅當left 和right 表達式所指代的內容一致時通過校驗 ;
right) 宏常用於校驗是否不同,當且僅當left 和right 表達式所指代的內容不同時通過校驗 ;
[dependencies]
near-sdk = "3.1.0"
3. 單元測試二
use near_sdk::borsh::{self, BorshDeserialize, BorshSerialize};
use near_sdk::collections::LookupMap;
use near_sdk::{env, near_bindgen};
如下是單元測試2 的代碼片段:
$ cargo test --package status-message
在第6 行的測試中,assert_eq 右邊的表達式利用合約方法get_status 嘗試從合約狀態數據中查詢StatusMessage 合約用戶francis.near 所對應的message 信息。但是由於代碼的第5 行僅僅初始化了整個合約的狀態,因此此時的合約數據整體為空,因此其返回值將是None。最終由於該結果符合預期,因此斷言正確,可以通過該單元測試。
test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in0.00s
4. 執行測試用例
$ cargo test --package status-message set_get_message
在編寫完上述單元測試後,我們還需要在該StatusMessage Rust 項目中配置該合約的Cargo.toml 文件,即在該文件的[dependencies] 小節中添加對near-sdk 的依賴(版本號具體為3.1. 0)。
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 1 filtered out; finished in0.00s
同時我們還需要在src/lib.rs 文件的開頭處導入這些來自於near_sdk 所提供的模塊或包:
在配置完合約項目的依賴後,我們便可以利用cargo 執行所有的單元測試用例。具體的命令如下: