You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

serde_json::to_string_pretty()序列化顺序随机致单元测试不稳定

问题原因与解决方法

核心原因

你遇到的测试结果不稳定问题,本质是直接比较JSON序列化后的字符串,而JSON对象的键顺序在规范中是无意义的。具体到你的场景:

  1. 即使启用了preserve_order特性,若json!宏生成的serde_json::Value背后仍使用无序的HashMap(比如特性未正确启用),Rust的HashMap会因运行时随机哈希种子导致键顺序每次运行都不同,从而出现测试时而通过时而失败的情况。
  2. 即便preserve_order生效,若结构体序列化时的字段顺序与json!宏内的键顺序不匹配,也可能因巧合出现偶尔匹配的情况,但这不是可靠的测试方式。

更优的解决方法

方法1:比较JSON语义而非字符串(推荐)

JSON的核心是键值对的集合,键顺序不影响语义。直接反序列化双方的内容为serde_json::Value后比较,完全规避键顺序问题,测试更健壮:

#[test]
fn test() {
    // 你的结构体实例生成逻辑
    let struct_instance = ...;
    let json_string = struct_instance.to_json_string(); // 你的结构体序列化方法

    // 预期的JSON值
    let expected = json!({
        "mock_key": "val",
        "another_key": "another_val",
    });

    // 反序列化实际输出为Value
    let actual: serde_json::Value = serde_json::from_str(&json_string).unwrap();

    // 比较语义等价性
    assert_eq!(actual, expected);
}

方法2:确保双方都使用有序结构(仅当你必须依赖键顺序时)

如果你确实需要验证键顺序(比如某些特定场景要求输出顺序),则需要确保预期值和结构体序列化都使用有序Map:

  1. 确认Cargo.toml中正确启用preserve_order特性:
serde_json = { version = "1.0", features = ["preserve_order"] }
  1. 手动构建有序的serde_json::Map作为预期值,而非依赖json!宏:
#[test]
fn test() {
    let struct_instance = ...;
    let json_string = struct_instance.to_json_string();

    // 手动构建有序Map
    let mut expected_map = serde_json::Map::new();
    expected_map.insert("mock_key".into(), "val".into());
    expected_map.insert("another_key".into(), "another_val".into());
    let expected_value = serde_json::Value::Object(expected_map);

    // 序列化预期值并比较
    assert_eq!(serde_json::to_string_pretty(&expected_value).unwrap(), json_string);
}

总结

优先选择方法1,因为它符合JSON的规范设计,测试逻辑更清晰且不受键顺序干扰。只有在业务强制要求输出键顺序时,才考虑方法2。

内容的提问来源于stack exchange,提问作者pigeon

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.24 00:05:15