考虑以下函数:
pub async fn api_get<T: 'static>(&self, path: &str) -> reqwest::Result<T>
where
T: serde::de::DeserializeOwned,
{
let url = format!("{}/{}", self.backend_url, path);
let response = self.client.get(&url).send().await?;
let data = response.json::<T>().await?;
Ok(data)
}
如何模拟 request::Result 类型以返回 Error 类型?
我尝试了以下方法:
#[tokio::test]
async fn foo_test() {
let mock_client = HttpClient::default();
mock_client
.expect_api_post()
.returning(|_, __| reqwest::Error::new(reqwest::ErrorKind::Other, "test error"));
}
但是,这不起作用,因为 new
方法是私有(private)的。那么我如何模拟这个函数的返回类型呢?
(在本例中,api_post 方法在 struct HttpClient
的 impl block 中声明)
最佳答案
不要直接使用库类型。相反,将库视为您真正需求的实现。可能会改变的实现。
将库视为纯粹的实现,测试变得更加容易。它还使程序结构更清晰,耦合性更小:不要直接传递来自 reqwest
的类型,而是定义一个仅公开您真正需要的功能的特征,然后使用 reqwest
> 实现它。然后做另一个 stub 实现作为测试的模拟(或使用类似 mockall
的东西)。
突然之间,单独测试组件变得非常容易,尤其是与组合相结合:只需将 stub 实现传递给测试者并检查其行为即可。
这种将库实现隐藏在您自己的特征后面的模式还可以减少相互依赖性并防止紧密耦合。您的应用程序不再依赖于特定的库。代码的其他部分只能使用这部分显式公开的内容。
如果您需要替换程序一部分的实现,这突然变得非常容易。
这听起来可能需要大量工作,而且烦人,需要编写大量样板文件,但实际上它确实不错 - 考虑到其优点,我认为这是值得的。在许多情况下,您可能实际上只需要一两个函数。在许多情况下,将库中的签名复制到您的特征中就足够了。确保包装库的错误类型(可能使用 thiserror
),或者使用 anyhow
之类的内容来轻松捕获所有错误处理,否则库的类型将溢出方式。
关于testing - Rust:如何模拟 request::Result?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/74507897/