【问题标题】:How to unit-test a deserialization function used in serde(deserialize_with)?如何对 serde(deserialize_with) 中使用的反序列化函数进行单元测试?
【发布时间】:2019-07-11 08:44:53
【问题描述】:

我有一个实现Deserialize 并在字段上使用serde(deserialize_with) 的结构:

#[derive(Debug, Deserialize)]
struct Record {
    name: String,
    #[serde(deserialize_with = "deserialize_numeric_bool")]
    is_active: bool,
}

deserialize_numeric_bool的实现将字符串“0”或“1”反序列化为对应的布尔值:

pub fn deserialize_numeric_bool<'de, D>(deserializer: D) -> Result<bool, D::Error>
    where D: Deserializer<'de>
{
    struct NumericBoolVisitor;

    impl<'de> Visitor<'de> for NumericBoolVisitor {
        type Value = bool;

        fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
            formatter.write_str("either 0 or 1")
        }

        fn visit_u64<E>(self, value: u64) -> Result<bool, E>
            where E: DeserializeError
        {
            match value {
                0 => Ok(false),
                1 => Ok(true),
                _ => Err(E::custom(format!("invalid bool: {}", value))),
            }
        }
    }

    deserializer.deserialize_u64(NumericBoolVisitor)
}

(感谢 cmets 关于代码改进)

我想为像deserialize_numeric_bool 这样的反序列化函数编写单元测试。当然,我友好的搜索框显示了serde_test crate 和documentation page about unit-testing。 但就我而言,这些资源对我没有帮助,因为 crate 测试了一个直接实现 Deserialize 的结构。

我的一个想法是创建一个新类型,它只包含我的反序列化函数的输出并用它进行测试。但这对我来说似乎是不必要的间接。

#[derive(Deserialize)]
NumericBool {
    #[serde(deserialize_with = "deserialize_numeric_bool")]
    value: bool
};

如何为它编写惯用的测试?

【问题讨论】:

  • 我对惯用的 Serde 了解不多,无法正确回答这个问题,但您可以通过像 serde-json 这样的板条箱实例化 Deserializer 实现,然后它会为您提供可以传递给的东西功能。如果在您的测试中使用 JSON crate 感觉很奇怪,您可以使用 serde-value 之类的东西。
  • 你找到更直接的解决方案了吗?

标签: unit-testing rust serde


【解决方案1】:

我当前的解决方案仅使用 serde 已经提供的结构。 在我的用例中,我只想测试给定的字符串是否会成功反序列化为 bool 或有一定的错误。 serde::de::value 为基本数据类型提供简单的反序列化器,例如 U64Deserializer 包含 u64。它还有一个Error struct,它为错误特征提供了一个最小的表示——准备好用于模拟错误。

我的测试look currently like that:我使用解串器模拟输入并将其传递给我的测试函数。我喜欢那里不需要间接,并且我没有其他依赖项。它不如assert_tokens* 提供的serde_test 好,因为它需要错误结构并且感觉不那么精致。但是对于我来说,只有一个值被反序列化,它满足了我的需求。

use serde::de::IntoDeserializer;
use serde::de::value::{U64Deserializer, StrDeserializer, Error as ValueError};

#[test]
fn test_numeric_true() {
    let deserializer: U64Deserializer<ValueError> = 1u64.into_deserializer();
    assert_eq!(numeric_bool(deserializer), Ok(true));
}

#[test]
fn test_numeric_false() {
    let deserializer: U64Deserializer<ValueError> = 0u64.into_deserializer();
    assert_eq!(numeric_bool(deserializer), Ok(false));
}

#[test]
fn test_numeric_invalid_number() {
    let deserializer: U64Deserializer<ValueError> = 2u64.into_deserializer();
    let error = numeric_bool(deserializer).unwrap_err();
    assert_eq!(error.description(), "invalid bool: 2");
}

#[test]
fn test_numeric_empty() {
    let deserializer: StrDeserializer<ValueError> = "".into_deserializer();
    let error = numeric_bool(deserializer).unwrap_err();
    assert_eq!(error.description(), "invalid type: string \"\", expected either 0 or 1");
}

我希望它也能帮助其他人或激励其他人找到更完善的版本。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-03-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多