Skip to content

Unit testing

The serde_test crate provides a convenient concise way to write unit tests for implementations of Serialize and Deserialize.

The Serialize impl for a value can be characterized by the sequence of Serializer calls that are made in the course of serializing the value, so serde_test provides a Token abstraction which corresponds roughly to Serializer method calls. It provides an assert_ser_tokens function to test that a value serializes into a particular sequence of method calls, an assert_de_tokens function to test that a value can be deserialized from a particular sequence of method calls, and an assert_tokens function to test both directions. It also provides functions to test expected failure conditions.

Here is an example from the linked-hash-map crate.

# #[allow(unused_imports)]
use linked_hash_map::LinkedHashMap;
#
# mod test {
#     use std::fmt;
#     use std::marker::PhantomData;
#
#     use serde::ser::{Serialize, Serializer, SerializeMap};
#     use serde::de::{Deserialize, Deserializer, Visitor, MapAccess};
#
use serde_test::{Token, assert_tokens};
#
#     // The version of linked-hash-map used by yaml-rust is not compatible
#     // with Serde 0.9, and Skeptic tests cannot have more than one version
#     // of any dependency. Reimplement a dumb immitation here.
#     #[derive(PartialEq, Debug)]
#     struct LinkedHashMap<K, V>(Vec<(K, V)>);
#
#     impl<K, V> LinkedHashMap<K, V> {
#         fn new() -> Self {
#             LinkedHashMap(Vec::new())
#         }
#
#         fn insert(&mut self, k: K, v: V) {
#             self.0.push((k, v));
#         }
#     }
#
#     impl<K, V> Serialize for LinkedHashMap<K, V>
#     where
#         K: Serialize,
#         V: Serialize,
#     {
#         fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
#         where
#             S: Serializer,
#         {
#             let mut map = serializer.serialize_map(Some(self.0.len()))?;
#             for &(ref k, ref v) in &self.0 {
#                 map.serialize_entry(k, v)?;
#             }
#             map.end()
#         }
#     }
#
#     struct LinkedHashMapVisitor<K, V>(PhantomData<fn() -> LinkedHashMap<K, V>>);
#
#     impl<'de, K, V> Visitor<'de> for LinkedHashMapVisitor<K, V>
#     where
#         K: Deserialize<'de>,
#         V: Deserialize<'de>,
#     {
#         type Value = LinkedHashMap<K, V>;
#
#         fn expecting(&self, _: &mut fmt::Formatter) -> fmt::Result {
#             unimplemented!()
#         }
#
#         fn visit_map<M>(self, mut access: M) -> Result<Self::Value, M::Error>
#         where
#             M: MapAccess<'de>,
#         {
#             let mut map = LinkedHashMap::new();
#             while let Some((key, value)) = access.next_entry()? {
#                 map.insert(key, value);
#             }
#             Ok(map)
#         }
#     }
#
#     impl<'de, K, V> Deserialize<'de> for LinkedHashMap<K, V>
#     where
#         K: Deserialize<'de>,
#         V: Deserialize<'de>,
#     {
#         fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
#         where
#             D: Deserializer<'de>,
#         {
#             deserializer.deserialize_map(LinkedHashMapVisitor(PhantomData))
#         }
#     }

#[test]
# fn skeptic_test_ser_de_empty() {}
fn test_ser_de_empty() {
    let map = LinkedHashMap::<char, u32>::new();

    assert_tokens(&map, &[
        Token::Map { len: Some(0) },
        Token::MapEnd,
    ]);
}

#[test]
# fn skeptic_test_ser_de() {}
fn test_ser_de() {
    let mut map = LinkedHashMap::new();
    map.insert('b', 20);
    map.insert('a', 10);
    map.insert('c', 30);

    assert_tokens(&map, &[
        Token::Map { len: Some(3) },
        Token::Char('b'),
        Token::I32(20),

        Token::Char('a'),
        Token::I32(10),

        Token::Char('c'),
        Token::I32(30),
        Token::MapEnd,
    ]);
}
#
#     pub fn run_tests() {
#         test_ser_de_empty();
#         test_ser_de();
#     }
# }
#
# fn main() {
#     test::run_tests();
# }