テスト

ソフトウェア開発、特にブロックチェーン開発において重要な部分はテストです。ここでは、Moveでのテストの基本と、Moveコードのテストの書き方と整理方法について説明します。

#[test] 属性

Moveのテストは、 #[test] 属性でマークされた関数です。この属性は、その関数がテスト関数であり、テストが実行される際に実行すべきであることをコンパイラに伝えます。テスト関数は通常の関数ですが、引数を取らず、返り値もありません。バイトコードからは除外され、公開されることはありません。

module book::testing {
    // テスト属性は `fun` キーワードの前に置かれます。
    // `#[test] fun my_test() { ... }` のように、上または直前に配置できます。
    // このテストの名前は`book::testing::simple_test`です。
    #[test]
    fun simple_test() {
        let sum = 2 + 2;
        assert!(sum == 4, 1);
    }

    // このテストの名前は `book::testing::more_advanced_test` です。
    #[test] fun more_advanced_test() {
        let sum = 2 + 2 + 2;
        assert!(sum == 4, 1);
    }
}

テストの実行

テストを実行するには、sui move testコマンドを使用します。このコマンドは、最初にパッケージをテストモードでビルドし、パッケージ内に見つかったすべてのテストを実行します。テストモードでは、sources/およびtests/ディレクトリのモジュールが処理され、テストが実行されます。

$ sui move test
> INCLUDING DEPENDENCY Sui
> INCLUDING DEPENDENCY MoveStdlib
> BUILDING book
> Running Move unit tests
> ...

#[expected_failure] でテスト失敗ケースを扱う

失敗ケースのテストは #[expected_failure] でマークできます。この属性を #[test] 関数に設置すると、テストが失敗することが期待されていることをコンパイラに伝えます。これは、特定の条件が満たされたときに関数が失敗することをテストしたい場合に便利です。

この属性は #[test] 関数にのみ設置可能です。

属性はabort(中止) code引数を取ることができ、テストが失敗したときに期待されるabort codeです。テストが異なるabort codeで失敗した場合、テストは失敗します。実行がabortしなかった場合も、テストは失敗します。

module book::testing_failure {

    const EInvalidArgument: u64 = 1;

    #[test]
    #[expected_failure(abort_code = 0)]
    fun test_fail() {
        abort 0 // 0でabortする
    }

    // 属性はまとめて設定することができます
    #[test, expected_failure(abort_code = EInvalidArgument)]
    fun test_fail_1() {
        abort 1 // 1でabortする
    }
}

abort_code 引数は、テストモジュールで定義された定数や他のモジュールからインポートされた定数を使用できます。これは、他のモジュールで定数を「アクセス」して使用できる唯一のケースです。

#[test_only] の有用性

テスト環境が内部関数や機能にアクセスできるようにすることは、テストプロセスを簡素化し、より徹底したテストを可能にします。ただし、これらの関数は最終的なパッケージには含まれるべきではないことを覚えておくことが重要です。このようなとき、 #[test_only] 属性が便利です。

module book::testing {
    // `secret` 関数を使用する公開関数
    public fun multiply_by_secret(x: u64): u64 {
        x * secret()
    }

    /// 一般には公開されていないプライベート関数
    fun secret(): u64 { 100 }

    #[test_only]
    /// この関数はテスト目的でのみテストや他の test-only 関数で使用可能です。
    /// 可視性に注意してください - `#[test_only]` の場合、一般に `public` 可視性を使用します。
    public fun secret_for_testing(): u64 {
        secret()
    }

    #[test]
    // テスト環境では `secret_for_testing` 関数にアクセスできます。
    fun test_multiply_by_secret() {
        let expected = secret_for_testing() * 2;
        assert!(multiply_by_secret(2) == expected, 1);
    }
}