隨機輸入 (Random Inputs)
Move 編譯器支援透過 #[random_test] 屬性,以隨機產生的輸入執行測試。這可啟用以屬性為基礎的測試,讓測試使用隨機 產生的值執行多次,以找出你可能不會想到要手動測試的邊界案例。
#[random_test] 屬性是用於測試輸入的編譯器功能,與用於鏈上隨機性的 sui::random 模組不同。
基本用法 (Basic Usage)
使用 #[random_test] 標記函式,並以基本型別宣告參數。執行測試時,測試執行器 會為每個參數產生隨機值。
module book::math;
public fun safe_add(a: u64, b: u64): u64 {
if (a > 0xFFFFFFFFFFFFFFFF - b) {
0xFFFFFFFFFFFFFFFF // 在最大值飽和
} else {
a + b
}
}
#[random_test]
fun test_safe_add_never_overflows(a: u64, b: u64) {
let result = safe_add(a, b);
// 結果應始終 >= 兩個輸入值(不會因溢位而環繞)
assert!(result >= a && result >= b);
}
支援的型別 (Supported Types)
隨機輸入適用於所有基本型別:
| 型別 | 產生範圍 |
|---|---|
| u8, u16, u32, u64, u128, u256 | 型別的完整範圍 |
| bool | true 或 false |
| address | 隨機的 32 位元組地址 |
| vector<T> | 具有隨機元素的隨機長度向量 |
注意:vector<T> 中的 T 必須是基本型別或另一個向量(例如 vector<vector<u8>>)。
實務技巧 (Practical Tips)
限制大型整數:若你的函式預期接收較小的值,請使用較小的型別並進行轉型:
#[random_test]
fun test_with_bounded_input(small: u8) {
let bounded = (small as u64) % 100; // 0-99 範圍
// ... 使用受限值進行測試
}
避免未受限的向量:vector<u8> 可能產生非常大的向量,導致測試緩慢或 gas 錯誤。建議使用固定大小的輸入,或手動建立向量:
// 避免:可能產生巨大的向量
#[random_test]
fun test_bad(v: vector<u8>) { /* ... */ }
// 較佳:控制大小
#[random_test]
fun test_good(a: u8, b: u8, c: u8) {
let v = vector[a, b, c];
// ... 使用已知大小的向量進行測試
}
補充而非取代:隨機測試可找出未預期的邊界案例,但可能遺漏特定 情境。請搭配針對性的單元測試使用:
use std::unit_test::assert_eq;
// 針對特定案例的測試
#[test]
fun test_add_zero() {
assert_eq!(safe_add(std::u64::max_value!(), 0), std::u64::max_value!());
}
// 用於一般屬性的隨機測試
#[random_test]
fun test_add_commutative(a: u64, b: u64) {
assert_eq!(safe_add(a, b), safe_add(b, a));
}
使用 assert_eq! 以利偵錯:隨機測試失敗時,你需要知道是哪組值 造成失敗。使用 assert_eq! 會在 失敗時印出兩個比較的值,使重現及偵錯問題更容易:
use std::unit_test::assert_eq;
#[random_test]
fun test_double(value: u64) {
let doubled = value * 2; // 這可能溢位,但為了簡潔起見,我們省略檢查。
// 失敗時會印出:"Assertion failed: <actual> != <expected>"
assert_eq!(doubled / 2, value);
}
控制測試執行 (Controlling Test Runs)
迭代次數 (Number of iterations)
預設情況下,隨機測試會使用不同輸入執行多次。使用 --rand-num-iters 控制 每個隨機測試執行的迭代次數:
# 每個隨機測試執行 100 次
sui move test --rand-num-iters 100
可重現的種子 (Reproducible seeds)
隨機測試失敗時,輸出會包含種子以及重現方式:
┌── test_that_failed ────── (seed = 2033439370411573084)
│ ...
│ 此測試使用隨機產生的輸入。請使用 `test test_that_failed --seed 2033439370411573084` 重新執行,以重現此測試失敗。
│
└──────────────────
使用提供的種子重現完全相同的失敗:
sui move test test_that_failed --seed 2033439370411573084
限制 (Limitations)
- 無範圍限制:你無法直接將隨機值限制為特定範圍;請如上所示使用模數 或型別轉型
- 向量大小:無法控制產生的向量長度
總結 (Summary)
- 使用 #[random_test](不是 #[test])為測試函式啟用隨機輸入
- 參數必須是基本型別或基本型別的向量
- 使用較小的型別和轉型來限制輸入,以避免極端值
- 使用 assert_eq! 以取得更佳的失敗診斷資訊
- 使用 --rand-num-iters 控制迭代次數,並使用 --seed 重現失敗
- 使用隨機測試來補充而非取代針對性的單元測試