Skip to main content

隨機輸入 (Random Inputs)

Move 編譯器支援透過 #[random_test] 屬性使用隨機輸入執行測試。這啟用了基於屬性的測試,其中測試會使用隨機生成的值多次執行,以發現你可能沒想到要手動測試的邊緣案例。

#[random_test] 屬性是用於測試輸入的編譯器功能,與用於鏈上隨機性的 sui::random 模組不同。

基本用法

#[random_test] 標記函式並宣告原始型別 (primitive types) 的參數。測試執行器在執行測試時會為每個參數生成隨機值。

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);
}

支援的型別

隨機輸入適用於所有原始型別:

型別生成範圍
u8, u16, u32, u64, u128, u256該型別的完整範圍
booltruefalse
address隨機 32 位元組地址
vector<T>隨機長度的向量,包含隨機元素

注意:vector<T> 中的 T 必須是原始型別或另一個向量 (例如,vector<vector<u8>>)。

實用技巧

限制大整數:如果你的函式預期較小的值,請使用較小的型別並進行轉型:

#[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(), 0), std::u64::max());
}

// 針對一般屬性的隨機測試
#[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);
}

控制測試運行

迭代次數

預設情況下,隨機測試會使用不同的輸入運行多次。使用 --rand-num-iters 來控制每個隨機測試運行的迭代次數:

# 每個隨機測試運行 100 次
sui move test --rand-num-iters 100

可重現的種子 (Reproducible seeds)

當隨機測試失敗時,輸出包含種子和重現說明:

┌── test_that_failed ────── (seed = 2033439370411573084)
│ ...
│ This test uses randomly generated inputs. Rerun with
│ `sui move test test_that_failed --seed 2033439370411573084`
│ to recreate this test failure.
└──────────────────

使用提供的種子來重現確切的失敗:

sui move test test_that_failed --seed 2033439370411573084

限制

  • 無範圍約束:你不能直接將隨機值限制在特定範圍內;如上所示使用模數運算 (modulo) 或型別轉型
  • 向量大小:無法控制生成的向量長度

總結

  • 使用 #[random_test] (而非 #[test]) 為測試函式啟用隨機輸入
  • 參數必須是原始型別或原始型別的向量
  • 使用較小的型別和轉型來限制輸入,以避免極端值
  • 使用 assert_eq! 以獲得更好的失敗診斷
  • 使用 --rand-num-iters 控制迭代次數,並使用 --seed 重現失敗
  • 使用隨機測試來補充,而不是取代針對性的單元測試