繁体   English   中英

如何使用 rust 特征来抽象 HTTP 测试调用?

[英]How do I use rust traits to abstract HTTP call for tests?

来自 Go 的许多接口可以用来执行以下操作:

async fn get_servers(client: &dyn std::marker::Send) -> Result<String, impl std::error::Error> {
   let servers_str = client.send().await?.text()
   let v: Value = serde_json::from_str(servers_str)?;
   
   println!("{:?}", v);
   Ok(servers_str.to_string())
   
}

// ...
get_servers(client.get(url))

我可以传入一些刚刚实现发送并返回文本的东西。 这样可以使代码可测试。 我想也许 send auto 特性会这样做,但显然不是。 说找不到发送。 也许某种 impl requestbuilder ?

一般来说,这是绝对可能的,(如果我错了,请纠正我)甚至建议。 这是一种称为依赖注入的编程范式。

简而言之,这意味着在您的情况下,通过接口(或 Rust: trait)传入依赖对象,以便您可以在测试时将其替换为不同类型的对象。

您在这里的错误是std::marker::Send特征并不像您认为的那样; 它将对象标记为可在线程之间传输。 它与std::marker::Sync密切相关,这意味着它可以被多个线程访问而不会导致竞争条件。

虽然许多库已经具有可用于该目的的特征,但在很多情况下,您必须设置自己的特征。 例如,在这里,我们有一个 hello world 函数,它通过用另一个专门用于测试的打印机替换它的打印机来进行测试。 如前所述,我们通过特征的抽象将打印机传递给 hello world 函数来实现这一点。

trait HelloWorldPrinter {
    fn print_text(&mut self, msg: &str);
}

struct ConsolePrinter;
impl HelloWorldPrinter for ConsolePrinter {
    fn print_text(&mut self, msg: &str) {
        println!("{}", msg);
    }
}

// This is the function we want to test.
// Note that we are using a trait here so we can replace the actual
// printer with a test mock when testing.
fn print_hello_world(printer: &mut impl HelloWorldPrinter) {
    printer.print_text("Hello world!");
}

fn main() {
    let mut printer = ConsolePrinter;
    print_hello_world(&mut printer);
}

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

    struct TestPrinter {
        messages: Vec<String>,
    }
    impl TestPrinter {
        fn new() -> Self {
            Self { messages: vec![] }
        }
    }
    impl HelloWorldPrinter for TestPrinter {
        fn print_text(&mut self, msg: &str) {
            self.messages.push(msg.to_string());
        }
    }

    #[test]
    fn prints_hello_world() {
        let mut printer = TestPrinter::new();
        print_hello_world(&mut printer);
        assert_eq!(printer.messages, ["Hello world!"]);
    }
}

进行cargo run时:

Hello world!

进行cargo test时:

     Running unittests src/main.rs

running 1 test
test tests::prints_hello_world ... ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

作为一个小解释,如果该代码没有自我解释:

  • 我们创建了一个特征HelloWorldPrinter ,这是我们的print_hello_world()函数唯一知道的。
  • 我们定义了一个ConsolePrinter结构,我们在运行时使用它来打印消息。 ConsolePrinter当然必须实现HelloWorldPrinter才能与print_hello_world()函数一起使用。
  • 为了测试,我们编写了我们使用的TestPrinter结构而不是ConsolePrinter 它不是打印,而是存储收到的内容,以便我们测试它是否传递了正确的消息。 当然, ConsolePrinter还必须实现HelloWorldPrinter特征才能与print_hello_world()一起使用。

我希望这会进入你的问题的方向。 如果您有任何问题,请随时进一步讨论。

我不能直接告诉你应该写什么来解决你的问题,因为你的问题很模糊,但这应该是你解决问题所需的工具集。 我希望。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM