serde_json::to_string_pretty()序列化顺序随机致单元测试不稳定
问题原因与解决方法
核心原因
你遇到的测试结果不稳定问题,本质是直接比较JSON序列化后的字符串,而JSON对象的键顺序在规范中是无意义的。具体到你的场景:
- 即使启用了
preserve_order特性,若json!宏生成的serde_json::Value背后仍使用无序的HashMap(比如特性未正确启用),Rust的HashMap会因运行时随机哈希种子导致键顺序每次运行都不同,从而出现测试时而通过时而失败的情况。 - 即便
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:
- 确认
Cargo.toml中正确启用preserve_order特性:
serde_json = { version = "1.0", features = ["preserve_order"] }
- 手动构建有序的
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
相关产品推荐
相关产品推荐

