Serde实现中的模拟实例

时间:2019-01-05 13:31:14

标签: unit-testing rust mocking serde

我正在尝试实现使用一些外部功能的自定义反序列化功能/方法。该函数创建一个实例并使用其方法。一切正常,但我不知道如何在测试中模拟服务。

更普遍的问题是:如何为反序列化功能/方法提供状态?

下面的代码说明了我的意思。

MagickBook是一个外部服务,它具有一个状态,并包含MagickBook::find方法中的一些基本逻辑。

Scroll是可反序列化的数据结构,应使用MagicBook中的逻辑反序列化。

在反序列化时,我想有一种方法可以从外部提供MagicBook的特定实例。例如在测试中。

Rust Playground

use serde::de::{Deserialize, Deserializer}; // 1.0.82
use serde_derive::Deserialize; // 1.0.82
use serde_json; // 1.0.33

struct MagickBook;

// Some service which I want to mock in the test
impl MagickBook {
    fn new() -> Self {
        Self {}
    }

    fn find(&self, _kind: &str) -> isize {
        let effect = 42;
        // Here we do some logic depending on input parameter
        // -- snip --
        return effect;
    }
}

#[derive(Deserialize, PartialEq, Debug)]
struct Scroll {
    #[serde(rename = "kind")]
    #[serde(deserialize_with = "deserialize_effect")]
    effect: isize,
}

fn deserialize_effect<'de, D>(deserializer: D) -> Result<isize, D::Error>
where
    D: Deserializer<'de>,
{
    let book = MagickBook::new();
    Ok(book.find(&String::deserialize(deserializer)?))
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn main() {
        let scroll: Scroll = serde_json::from_str("{\"kind\":\"wind\"}").unwrap();
        assert_eq!(scroll, Scroll { effect: 42 });
    }
}

1 个答案:

答案 0 :(得分:1)

我建议通过内部可变的线程本地实例访问模拟。


use serde::{Deserialize, Deserializer};
use std::cell::RefCell;

struct MagickBook {
    value: isize,
}

thread_local! {
    static MAGICK_BOOK: RefCell<MagickBook> = RefCell::new(MagickBook::new());
}

impl MagickBook {
    fn new() -> Self {
        MagickBook { value: 0 }
    }

    fn find(&self, _kind: &str) -> isize {
        let effect = self.value;
        // -- snip --
        effect
    }
}

#[derive(Deserialize, PartialEq, Debug)]
struct Scroll {
    #[serde(rename = "kind", deserialize_with = "deserialize_effect")]
    effect: isize,
}

fn deserialize_effect<'de, D>(deserializer: D) -> Result<isize, D::Error>
where
    D: Deserializer<'de>,
{
    let kind = String::deserialize(deserializer)?;
    Ok(MAGICK_BOOK.with(|book| book.borrow().find(&kind)))
}

#[test]
fn test_deserialize() {
    MAGICK_BOOK.with(|book| book.borrow_mut().value = 42);
    let scroll: Scroll = serde_json::from_str(r#"{"kind":"wind"}"#).unwrap();
    assert_eq!(scroll, Scroll { effect: 42 });
}