[英]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.