testing - Rust:如何模拟 request::Result?

标签 testing rust mocking

考虑以下函数:

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/

相关文章:

javascript - 在 Typescript 源代码与生成的 JavaScript 上运行测试

arrays - 修改结构中数组的最佳方法?

websocket - 在BusReader和Warp WebSocket接收器之间转发消息会留下未占用的缓冲区?

unit-testing - MockDomain 调用提供夹具域数据不起作用,我错过了什么? Grails 2.0.1

java - 无法使用模拟来抛出异常 - 抛出的异常未被捕获

testing - 调试测试 - 从关闭测试浏览器窗口获取 Testem/Qunit

android - 使用单个命令从多模块项目运行所有 UI/单元测试

symfony - Silex - 从测试中分派(dispatch)时不执行监听器

vector - 在传递给不安全代码之前正确初始化向量

typescript - 使用 Jest 正确模拟库/方法