测试具有"with"属性的serde序列化/反序列化函数

5
Serde 衍生宏具有通过 #[serde(with = "module")] 字段属性控制字段序列化/反序列化的能力。其中 "module" 应该具有正确参数和返回类型的序列化和反序列化函数。
一个不幸过于矫揉造作的例子:(原文链接)
use serde::{Deserialize, Serialize};

#[derive(Debug, Default, PartialEq, Eq)]
pub struct StringPair(String, String);

mod stringpair_serde {
    pub fn serialize<S>(sp: &super::StringPair, ser: S) -> Result<S::Ok, S::Error>
    where
        S: serde::Serializer,
    {
        ser.serialize_str(format!("{}:{}", sp.0, sp.1).as_str())
    }

    pub fn deserialize<'de, D>(d: D) -> Result<super::StringPair, D::Error>
    where
        D: serde::Deserializer<'de>,
    {
        d.deserialize_str(Visitor)
    }

    struct Visitor;

    impl<'de> serde::de::Visitor<'de> for Visitor {
        type Value = super::StringPair;

        fn expecting(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
            write!(f, "a pair of strings separated by colon (:)")
        }

        fn visit_str<E>(self, s: &str) -> Result<Self::Value, E>
        where
            E: serde::de::Error,
        {
            Ok(s.split_once(":")
                .map(|tup| super::StringPair(tup.0.to_string(), tup.1.to_string()))
                .unwrap_or(Default::default()))
        }
    }
}

#[derive(Serialize, Deserialize)]
struct UsesStringPair {
    // Other fields ...

    #[serde(with = "stringpair_serde")]
    pub stringpair: StringPair,
}

fn main() {
    let usp = UsesStringPair {
        stringpair: StringPair("foo".to_string(), "bar".to_string()),
    };

    assert_eq!(
        serde_json::json!(&usp).to_string(),
        r#"{"stringpair":"foo:bar"}"#
    );

    let usp: UsesStringPair = serde_json::from_str(r#"{"stringpair":"baz:qux"}"#).unwrap();

    assert_eq!(
        usp.stringpair,
        StringPair("baz".to_string(), "qux".to_string())
    )
}

测试UsesStringPair的派生序列化很容易进行简单的断言。但是我也看过serde_test示例,因为那对我来说也有意义。

然而,我希望能够独立测试stringpair_serde::{serialize, deserialize}函数(例如,如果我的crate只提供mycrate::StringPairmycrate::stringpair_serde,而UsesStringPair是供crate用户实现的)。

我研究了一种方法,就是创建一个serde_json::Serializer(使用new,需要一个io::Write实现,我无法轻松地创建和使用它,但这是一个单独的问题),然后使用创建的Serializer调用serialize,在结果上进行断言,就像以前一样。然而,这并没有测试serde::Serializer的任何/所有实现,只是在serde_json中提供的一个实现。

我想知道是否有一种像serde_test示例中适用于由模块提供的ser/deser函数的方法。


为什么不直接为StringPair派生这两个特征呢? - Ruifeng Xie
1
对于你的另一个(暗示的)问题,&mut Vec<u8> 实现了 io::Write - user4815162342
@RuifengXie 有几个原因:序列化相同时间可能有不同的方法。我能想到的一个例子是 chrono::DateTime<Tz>。它可以被序列化为字符串、Unix 时间戳、带有更高分辨率的整数时间戳(例如毫秒等)。因此,该类型本身实现了 Serialize、Deserialize 特性,但 chrono 还提供了子模块,可用于使用 with = ... 以不同的方式序列化 DateTime 值。 - Zia Ur Rehman
它也可能是因为箱子不提供实际类型,只提供了在其他地方定义的类型的序列化/反序列化格式(无法在其他地方实现特征或派生)。另一个相关但不同的情况是:模块编写者提供了类型,例如StringPair,并且它确实实现了ser/de特征,但模块编写者还希望为该类型的单调容器提供ser/de,例如Option<StringPair>StringPair的序列化方式不同。有许多原因需要一个独立的ser/de模块而不是类型实现特征。 - Zia Ur Rehman
1个回答

1

serde_test 中没有直接测试这些函数的方法。虽然 serde_test 在内部使用自己的 SerializerDeserializer,但它不会公开这些类型,因此您无法在测试中直接使用它们。

但是,您可以使用 serde_assert crate(免责声明:我编写了 serde_assert)来完成此操作。 serde_assert 专门为测试直接公开其 SerializerDeserializer,非常适合您的用例:

use serde_assert::{Deserializer, Serializer, Token, Tokens};

#[test]
fn serialize() {
    let serializer = Serializer::builder().build();

    assert_eq!(
        stringpair_serde::serialize(
            &StringPair("foo".to_string(), "bar".to_string()),
            &serializer
        ),
        Ok(Tokens(vec![Token::Str("foo:bar".to_string())])),
    );
}

#[test]
fn deserialize() {
    let mut deserializer = Deserializer::builder()
        .tokens(Tokens(vec![Token::Str("foo:bar".to_string())]))
        .build();

    assert_eq!(
        stringpair_serde::deserialize(&mut deserializer),
        Ok(StringPair("foo".to_string(), "bar".to_string()))
    );
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接