Skip to content

12. 测试

在软件开发中,测试是至关重要的环节,尤其是在区块链开发中。Move 语言提供了一套内置的测试框架,帮助开发者编写和运行测试用例,确保代码的正确性和可靠性。

#[test] 属性

在 Move 中,测试函数使用 #[test] 属性进行标记。编译器会识别标记了 #[test] 的函数,并在运行测试时执行这些函数。测试函数是普通函数,但必须满足以下条件:

  • 没有参数。
  • 没有返回值。

测试函数会被排除在最终的字节码之外,不会被发布到区块链上。

示例:

move
module book::testing {
    #[test]
    fun simple_test() {
        let sum = 2 + 2;
        assert!(sum == 4);
    }

    #[test]
    fun more_advanced_test() {
        let sum = 2 + 2 + 2;
        assert!(sum == 6); 
    }
}

运行测试

可以使用 sui move test 命令运行测试。此命令会以测试模式构建包,并执行包中所有标记为 #[test] 的测试函数。

  • 测试模式下,sources/ 和 tests/ 目录中的模块都会被包含。
  • 测试结果会显示通过和失败的情况。

运行测试:

sh
sui move test

assert! 宏

assert! 宏是 Move 测试框架中最重要的工具之一。它用于检查某个条件是否为真。如果条件为假,测试会立即中止,并抛出错误。

语法:

move
assert!(<condition>, <abort_code>);
  • <condition>:需要断言的布尔表达式。
  • <abort_code>(可选):如果断言失败,抛出的错误码。

示例:

move
#[test]
fun test_assert() {
    let sum = 2 + 2;
    assert!(sum == 4, 100); // 如果 sum 不等于 4,抛出错误码 100
}

#[expected_failure] 属性

#[expected_failure] 属性用于标记预期会失败的测试用例。这在测试会触发错误或中止的代码路径时非常有用。

示例:

move
#[test]
#[expected_failure(abort_code = 0)]
fun test_fail() {
    abort 0; // 预期抛出错误码 0
}

#[test_only] 属性

在测试环境中,有时需要访问一些内部函数或特殊功能来简化测试过程。Move 提供了 #[test_only] 属性,确保这些函数仅在测试环境中可用,不会包含在最终发布的字节码中。

示例:

move
module book::testing {
    public fun multiply_by_secret(x: u64): u64 {
        x * secret()
    }

    fun secret(): u64 {
        100
    }

    #[test_only]
    public fun secret_for_testing(): u64 {
        secret()
    }

    #[test]
    fun test_multiply_by_secret() {
        let expected = secret_for_testing() * 2;
        assert!(multiply_by_secret(2) == expected);
    }
}

测试最佳实践

  1. 尽量覆盖所有代码路径:确保主要功能和异常处理的代码路径都被测试。
  2. 使用 #[expected_failure] 测试错误路径:验证代码是否按照预期抛出错误。
  3. 避免依赖全局状态:测试函数应保持独立,以便测试的可预测性。
  4. 编写简单清晰的测试用例:测试代码应易于阅读,便于维护。

总结

  • #[test]:标记测试函数。
  • assert!:断言条件是否为真,失败时抛出错误。
  • #[expected_failure]:测试预期失败的代码路径。
  • #[test_only]:标记仅用于测试环境的函数。
  • Move 测试框架提供了强大的工具来确保代码的正确性和稳定性。编写全面的测试用例可以帮助尽早发现问题,提升代码质量。