httpmock Rust 的 HTTP 模拟库。

文摘   2024-08-10 00:40   江苏  

httpmock

HTTP 模拟库,为 Rust 语言设计。

  • 文档:Documentation[1]
  • 包:Crate[2]
  • 报告错误:Report Bug[3]
  • 功能请求:Request Feature[4]
  • 更新日志:Changelog[5]
  • 支持项目:Support this Project[6]

特性

  • 简单、表达性强、流畅的 API。
  • 内置多种辅助工具,便于请求匹配(正则表达式、JSON、序列化、cookies 等)。
  • 并行测试执行。
  • 可扩展的请求匹配。
  • 完全异步的核心,提供同步和异步 API。
  • 高级验证和调试支持(包括实际和预期 HTTP 请求值之间的差异生成)。
  • 故障和网络延迟模拟。
  • 支持正则表达式匹配、JSON、序列化、cookies 等。
  • 独立模式,附带Docker 镜像[7]
  • 支持使用 YAML 文件配置模拟[8]

开始使用

Cargo.toml中添加httpmock

[dev-dependencies]
httpmock = "0.7.0"

然后可以按照以下方式使用httpmock

use httpmock::prelude::*;

// 启动轻量级模拟服务器。
let server = MockServer::start();

// 在服务器上创建一个模拟。
let mock = server.mock(|when, then| {
    when.method(GET)
        .path("/translate")
        .query_param("word""hello");
    then.status(200)
        .header("content-type""text/html; charset=UTF-8")
        .body("Привет");
});

// 向模拟服务器发送HTTP请求。这模拟了您的代码。
let response = isahc::get(server.url("/translate?word=hello")).unwrap();

// 确保指定的模拟被调用了恰好一次(或者以详细的错误描述失败)。
mock.assert();

// 确保模拟服务器的响应如指定的那样。
assert_eq!(response.status(), 200);

上述示例将启动一个轻量级的 HTTP 模拟服务器,并配置它响应所有GET请求到路径/translate,查询参数为word=hello。相应的 HTTP 响应将包含文本体Привет

如果请求失败,httpmock将显示包括预期和实际 HTTP 请求之间的差异的详细错误描述:

差异图像

详细文档

Crate httpmock

开始使用

Cargo.toml 中添加 httpmock

[dev-dependencies]
httpmock = "0.7.0-rc.1"

使用示例:

use httpmock::prelude::*;

// 启动一个轻量级模拟服务器。
let server = MockServer::start();
// 在服务器上创建一个模拟。
let hello_mock = server.mock(|when, then| {
    when.method(GET)
        .path("/translate")
        .query_param("word""hello");
    then.status(200)
        .header("content-type""text/html")
        .body("ohi");
});

使用方法

首先需要启动一个模拟服务器,通过调用 MockServer::start。这将在后台启动一个轻量级 HTTP 模拟服务器,并等待服务器准备好接受请求。

然后,可以使用 MockServer::mock 方法在服务器上创建一个 Mock 对象。该方法期望一个闭包,包含两个参数,我们称之为 whenthen 参数:

  • when 参数是 When 类型,包含所有请求特征。模拟服务器只会响应符合所有标准的 HTTP 请求。否则,将返回 HTTP 状态码 404 和错误消息。
  • then 参数是 Then 类型,包含模拟服务器将响应的所有值。

同步 / 异步

httpmock 的内部实现完全异步。尽管如此,它提供了同步和异步 API。如果你想手动安排等待操作,可以使用每个可能阻塞操作的异步变体。例如,MockServer::start_asyncMockServer::start 的异步对应版本。你可以在整个库中找到类似的方法。

并行性

为了平衡执行速度和资源消耗,模拟服务器在内部服务器池中保持。这允许在不通过创建太多 HTTP 服务器而压垮执行机器的情况下并行运行测试。如果测试尝试使用 MockServer(例如,通过调用 MockServer::start)而服务器池为空(即所有服务器都被其他测试占用),则测试将被阻塞。

// 向模拟服务器发送 HTTP 请求。这模拟了你的代码。
let response = isahc::get(server.url("/translate?word=hello")).unwrap();
// 确保指定的模拟被调用了恰好一次(或者失败)。
hello_mock.assert();
// 确保模拟服务器按指定响应。
assert_eq!(response.status(), 200);

模拟服务器永远不会被重新创建,而是被回收/重置。池按需填充,最多可包含 25 个服务器。你可以使用环境变量 HTTPMOCK_MAX_SERVERS 覆盖此数字。

调试

httpmock 使用 log crate 进行日志记录。这允许你看到包含有关 httpmock 行为的详细信息的详细日志输出。你可以使用这些日志输出来调查问题,例如找出为什么一个请求不匹配模拟定义。

最有用的日志级别是 debug,但你也可以降低到 trace 以查看更多信息。

注意:要能够看到日志输出,你需要在启动测试执行时添加 --nocapture 参数!

提示:如果你使用 env_logger 后端,你需要设置 RUST_LOG 环境变量为 httpmock=debug

API 替代方案

这个库提供了两个功能上可互换的 DSL API,允许你在服务器上创建模拟。你可以选择你最喜欢的一个,或者两者并行使用。为了保持一致的外观,建议坚持使用其中之一。

When/Then API

这是 httpmock 的默认 API。它简洁易读。主要目标是将库带来的开销减少到最低。它与格式化工具(如 rustfmt,即 cargo fmt)很好地配合工作,并且可以完全受益于 IDE 支持。

示例:

let server = httpmock::MockServer::start();
let greeting_mock = server.mock(|when, then| {
    when.path("/hi");
    then.status(200);
});
let response = isahc::get(server.url("/hi")).unwrap();
greeting_mock.assert();

注意 whenthen 是变量。这允许你将它们重命名为你更喜欢的名称(例如 expect/respond_with)。

这个 API 的相关元素是 MockServer::mockWhenThen

示例可以在该 crate 的 Git 仓库的测试目录中找到。

独立模式

你可以使用 httpmock 运行一个独立的模拟服务器,它在一个单独的进程中运行。这允许它对多个应用程序可用,不仅仅在你的单元和集成测试中。这在你想在系统(甚至端到端)测试中使用 httpmock,需要模拟服务时非常有用。有了这个功能,httpmock 是一个在开发生命周期的所有阶段都非常有用的通用 HTTP 模拟工具。

使用独立模拟服务器

尽管你可以自己构建独立模式的模拟服务器,但使用附带的 Docker 镜像是最简单的。

为了能够在测试中使用独立服务器,你需要改变创建 MockServer 实例的方式。不是使用 MockServer::start,而是需要使用其中一个连接方法(如 MockServer::connectMockServer::connect_from_env)连接到远程服务器。注意:这些仅在启用远程特性时可用。

use httpmock::prelude::*;
use isahc::get;

#[test]
fn simple_test() {
    // Arrange
    let server = MockServer::connect("some-host:5000");
    let hello_mock = server.mock(|when, then| {
        when.path("/hello/standalone");
        then.status(200);
    });
    // Act
    let response = get(server.url("/hello/standalone")).unwrap();
    // Assert
    hello_mock.assert();
    assert_eq!(response.status(), 200);
}

独立并行性

为了防止与其他测试的干扰,测试函数被迫顺序使用独立模拟服务器。这意味着当连接到远程服务器时,测试函数可能会被阻塞,直到服务器再次空闲。这与使用本地模拟服务器的测试形成对比。

独立模式的限制

目前,无法将自定义请求匹配器与独立模拟服务器结合使用(见 When::matchesMock::expect_match)。

使用 YAML 模拟定义文件的独立模式

独立服务器还可以在启动时从 YAML 文件中读取模拟定义,并在服务器再次关闭之前提供模拟端点。这些静态模拟在运行时不能被删除(即使是使用模拟服务器的基于 Rust 的测试),并且在模拟服务器的整个正常运行时间内存在。

定义文件遵循你在常规 Rust 测试中也会使用的 httpmock 标准 API。可以在 httpmock Github 仓库的测试目录中找到示例模拟定义文件。

你可以按以下方式启动支持静态模拟的模拟服务器:

如果你使用该创建仓库或 Dockerhub 中的 Docker 镜像,你只需要将包含所有模拟规范文件的卷挂载到容器内的 /mocks 目录。如果你从源代码构建 httpmock 并使用二进制文件,那么你可以传递包含所有模拟规范文件的目录路径,使用 --static-mock-dir 参数。示例:httpmock --expose --static-mock-dir=/mocks

示例

您可以在`httpmock`测试目录[9]中找到示例。参考文档[10] 也包含了许多示例。还有一个在线教程[11]

参考资料
[1]

Documentation: https://docs.rs/httpmock/

[2]

Crate: https://crates.io/crates/httpmock

[3]

Report Bug: https://github.com/alexliesenfeld/httpmock/issues

[4]

Request Feature: https://github.com/alexliesenfeld/httpmock/issues

[5]

Changelog: https://github.com/alexliesenfeld/httpmock/blob/master/CHANGELOG.md

[6]

Support this Project: https://github.com/sponsors/alexliesenfeld

[7]

Docker 镜像: https://hub.docker.com/r/alexliesenfeld/httpmock

[8]

配置模拟: https://github.com/alexliesenfeld/httpmock/tree/master#file-based-mock-specification

[9]

httpmock测试目录: https://github.com/alexliesenfeld/httpmock/blob/master/tests/

[10]

参考文档: https://docs.rs/httpmock/

[11]

在线教程: https://alexliesenfeld.com/mocking-http-services-in-rust


编程悟道
自制软件研发、软件商店,全栈,ARTS 、架构,模型,原生系统,后端(Node、React)以及跨平台技术(Flutter、RN).vue.js react.js next.js express koa hapi uniapp Astro
 最新文章