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);
}
}
测试最佳实践
- 尽量覆盖所有代码路径:确保主要功能和异常处理的代码路径都被测试。
- 使用 #[expected_failure] 测试错误路径:验证代码是否按照预期抛出错误。
- 避免依赖全局状态:测试函数应保持独立,以便测试的可预测性。
- 编写简单清晰的测试用例:测试代码应易于阅读,便于维护。
总结
- #[test]:标记测试函数。
- assert!:断言条件是否为真,失败时抛出错误。
- #[expected_failure]:测试预期失败的代码路径。
- #[test_only]:标记仅用于测试环境的函数。
- Move 测试框架提供了强大的工具来确保代码的正确性和稳定性。编写全面的测试用例可以帮助尽早发现问题,提升代码质量。