使用模拟客户端编写测试

了解如何对真实客户端实现进行桩化,以注入用于单元测试的模拟对象。 使用 Google Cloud Client Libraries for Rust 的模拟客户端,您可以 编写受控的可靠单元测试,这些测试不会进行网络调用,也不会产生 结算费用。

依赖项

Rust 有多种模拟框架。本指南使用 mockall。将其添加为开发依赖项:

cargo add --dev mockall

此外,本指南使用 Speech 客户端,以便更轻松地理解 示例(但这些概念适用于所有客户端)。

将所需的依赖项添加到 Cargo.toml 文件:

cargo add google-cloud-speech-v2 google-cloud-lro

模拟客户端

如需使用模拟客户端测试代码,您可以定义模拟结构体,为测试场景配置其预期行为,然后将该模拟对象注入到应用逻辑中。以下示例演示了此工作流。

首先,添加 use 语句以简化代码:

use google_cloud_gax as gax;
use google_cloud_speech_v2 as speech;

假设应用有一个函数,该函数使用 Speech 客户端调用 GetRecognizer,设置请求的 name 字段,并处理服务器响应。

// An example application function.
//
// It makes an RPC, setting some field. In this case, it is the `GetRecognizer`
// RPC, setting the name field.
//
// It processes the response from the server. In this case, it extracts the
// display name of the recognizer.
async fn my_application_function(client: &speech::client::Speech) -> gax::Result<String> {
    client
        .get_recognizer()
        .set_name("invalid-test-recognizer")
        .send()
        .await
        .map(|r| r.display_name)
}

您可以测试应用如何处理来自服务的不同响应。

接下来,定义模拟结构体。此结构体实现了 speech::stub::Speech 特征。

mockall::mock! {
    #[derive(Debug)]
    Speech {}
    impl speech::stub::Speech for Speech {
        async fn get_recognizer(&self, req: speech::model::GetRecognizerRequest, _options: gax::options::RequestOptions) -> gax::Result<gax::response::Response<speech::model::Recognizer>>;
    }
}

创建模拟对象的实例。请注意, mockall::mock! 宏会在之前定义的结构体的名称前添加 Mock 前缀。

let mut mock = MockSpeech::new();

设置模拟对象的预期行为。例如,预期代码会使用特定名称调用 GetRecognizer,并模拟来自服务的成功响应。

mock.expect_get_recognizer()
    .withf(move |r, _|
        // Optionally, verify fields in the request.
        r.name == "invalid-test-recognizer")
    .return_once(|_, _| {
        Ok(gax::response::Response::from(
            speech::model::Recognizer::new().set_display_name("test-display-name"),
        ))
    });

使用模拟对象创建 Speech 客户端:

let client = speech::client::Speech::from_stub(mock);

调用函数:

let display_name = my_application_function(&client).await?;

验证结果:

assert_eq!(display_name, "test-display-name");

模拟错误

模拟错误与模拟成功类似。如需模拟错误,请修改模拟对象返回的结果。

mock.expect_get_recognizer().return_once(|_, _| {
    // This time, return an error.
    use gax::error::Error;
    use gax::error::rpc::{Code, Status};
    let status = Status::default()
        .set_code(Code::NotFound)
        .set_message("Resource not found");
    Err(Error::service(status))
});

使用 from_stub() 构建的客户端没有内部重试循环;它会将桩的所有错误直接返回给应用。

后续步骤

如需查看本指南中的完整代码,请参阅 GitHub 上 google-cloud-rust 代码库中的源文件