iT邦幫忙

2022 iThome 鐵人賽

DAY 5
0
Web 3

Smart-Contract Language: Move系列 第 25

Day 25 Advanced Topics: Unit Test

  • 分享至 

  • xImage
  •  

Move 的單元測試為 Move 源語言添加了三個新註釋

它們分別將函數標記為測試,將模塊或模塊成員(use、函數或結構)標記為僅用於測試的代碼,並標記預期測試將失敗。這些註釋可以放置在具有任何可見性的函數上。每當一個模塊或模塊成員被註釋為#[test_only]or#[test]時,它不會被包含在編譯中,除非它被編譯用於測試。

  • #[test]
    • 只能放在沒有參數的函數上
  • #[test_only]
  • #[expected_failure]

example

#[test] // OK
fun this_is_a_test() { ... }

#[test] // 失敗,因為有參數
fun this_is_not_correct(arg: signer) { ... }

測試也可以註釋為#[expected_failure] 此註釋標誌著測試會引發錯誤。可以通過使用註釋來確保測試使用特定的中止代碼中止#[expected_failure(abort_code = <code>)]。只有具有#[test]註釋的函數也可以註釋為 # [expected_failure]

example

#[test]
#[expected_failure]
public fun this_test_will_abort_and_pass() { abort 1 }

#[test]
#[expected_failure]
public fun test_will_error_and_pass() { 1/0; }

#[test]
#[expected_failure(abort_code = 0)]
public fun test_will_error_and_fail() { 1/0; }

#[test, expected_failure] // Can have multiple in one attribute. This test will pass.
public(script) fun this_other_test_will_abort_and_pass() { abort 1 }

帶有參數的測試

帶有參數的測試註解採用#[test(<param_name_1> = <address>, ..., <param_name_n> = <address>)]. 如果以這種方式註釋函數,則函數的參數必須是參數 < 的排列param_name_1>, ..., <param_name_n>,即這些參數在函數中出現的順序和它們在測試註釋中的順序不必相同,但它們必須能夠通過名稱相互匹配。

#[test(arg = @0xC0FFEE)] // OK
fun this_is_correct_now(arg: signer) { ... }

#[test(wrong_arg_name = @0xC0FFEE)] // Not correct: arg name doesn't match
fun this_is_incorrect(arg: signer) { ... }

#[test(a = @0xC0FFEE, b = @0xCAFE)] // OK. We support multiple signer arguments, but you must always provide a value for that argument
fun this_works(a: signer, b: signer) { ... }

// somewhere a named address is declared
#[test_only] // test-only named addresses are supported
address TEST_NAMED_ADDR = @0x1;
...
#[test(arg = @TEST_NAMED_ADDR)] // Named addresses are supported!
fun this_is_correct_now(arg: signer) { ... }

Test only

一個模塊及其任何成員都可以聲明為僅測試。在這種情況下,只有在測試模式下編譯時,該項目才會包含在編譯後的 Move 字節碼中。use此外,在測試模式之外編譯時,模塊的任何非 test#[test_only]都會在編譯期間引發錯誤。

#[test_only] // test only attributes can be attached to modules
module ABC { ... }

#[test_only] // test only attributes can be attached to named addresses
address ADDR = @0x1;

#[test_only] // .. to uses
use 0x1::SomeOtherModule;

#[test_only] // .. to structs
struct SomeStruct { ... }

#[test_only] // .. and functions. Can only be called from test code, but not a test
fun test_only_function(...) { ... }

運行單元測試

可以使用move package test 指令運行 Move 包的單元測試。

$ move package -h

example

  1. 還記得上一篇介紹的 package 嗎,我們創建一個空的來進行測試
$ move package new TestExample;
$ cd TestExample
  1. 接下來在sources目錄下添加以下模塊:
// filename: sources/MyModule.move
module 0x1::MyModule {

    struct MyCoin has key { value: u64 }

    public fun make_sure_non_zero_coin(coin: MyCoin): MyCoin {
        assert!(coin.value > 0, 0);
        coin
    }

    public fun has_coin(addr: address): bool {
        exists<MyCoin>(addr)
    }

    #[test]
    fun make_sure_non_zero_coin_passes() {
        let coin = MyCoin { value: 1 };
        let MyCoin { value: _ } = make_sure_non_zero_coin(coin);
    }

    #[test]
    // Or #[expected_failure] if we don't care about the abort code
    #[expected_failure(abort_code = 0)]
    fun make_sure_zero_coin_fails() {
        let coin = MyCoin { value: 0 };
        let MyCoin { value: _ } = make_sure_non_zero_coin(coin);
    }

    #[test_only] // test only helper function
    fun publish_coin(account: &signer) {
        move_to(account, MyCoin { value: 1 })
    }

    #[test(a = @0x1, b = @0x2)]
    fun test_has_coin(a: signer, b: signer) {
        publish_coin(&a);
        publish_coin(&b);
        assert!(has_coin(@0x1), 0);
        assert!(has_coin(@0x2), 1);
        assert!(!has_coin(@0x3), 1);
    }
}

運行測試

$ move package test
BUILDING MoveStdlib
BUILDING TestExample
Running Move unit tests
[ PASS    ] 0x1::MyModule::make_sure_non_zero_coin_passes
[ PASS    ] 0x1::MyModule::make_sure_zero_coin_fails
[ PASS    ] 0x1::MyModule::test_has_coin
Test result: OK. Total tests: 3; passed: 3; failed: 0

單獨測試

只測試包含特定名稱的函數

-f <str>或者--filter <str>

只測試包含 zero_coin 名字的函數

$ move package test -f zero_coin
CACHED MoveStdlib
BUILDING TestExample
Running Move unit tests
[ PASS    ] 0x1::MyModule::make_sure_non_zero_coin_passes
[ PASS    ] 0x1::MyModule::make_sure_zero_coin_fails
Test result: OK. Total tests: 2; passed: 2; failed: 0

詳細得測試資料

收集有關測試運行的統計資料

$ move package test -s
CACHED MoveStdlib
BUILDING TestExample
Running Move unit tests
[ PASS    ] 0x1::MyModule::make_sure_non_zero_coin_passes
[ PASS    ] 0x1::MyModule::make_sure_zero_coin_fails
[ PASS    ] 0x1::MyModule::test_has_coin

Test Statistics:

┌───────────────────────────────────────────────┬────────────┬───────────────────────────┐
│                   Test Name                   │    Time    │   Instructions Executed   │
├───────────────────────────────────────────────┼────────────┼───────────────────────────┤
│ 0x1::MyModule::make_sure_non_zero_coin_passes │   0.009    │             1             │
├───────────────────────────────────────────────┼────────────┼───────────────────────────┤
│ 0x1::MyModule::make_sure_zero_coin_fails      │   0.008    │             1             │
├───────────────────────────────────────────────┼────────────┼───────────────────────────┤
│ 0x1::MyModule::test_has_coin                  │   0.008    │             1             │
└───────────────────────────────────────────────┴────────────┴───────────────────────────┘

Test result: OK. Total tests: 3; passed: 3; failed: 0

在區塊鏈上,需要更加小心所撰寫的程式碼是否有任何非預期的 BUG, 良好的測試和覆蓋率可以盡量減低風險,本篇一定要好好掌握。讓我們 Move to Day 26


上一篇
Day 24 Advanced Topics: Standard Library
下一篇
Day 26 回顧
系列文
Smart-Contract Language: Move30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言