
前言:
在本文中我们将探讨在 Rust 的开发下, 如何的针对 Rest API 进行集成测试。
阅读完本文后, 你将能了解:
- 使用 actix-web 开发端点 (endpoint); /health_check。
- 针对端点; /health_check; 进行集成测试。
本文:
当我们收到一个 GET 的 /health_check 请求时, 我们将只返回个 200 OK 的响应; 没有响应信息的本文 (body)。
集成测试会进行以下的检验:
- health_check 的端点是 /health_check。
- health_check 的 http 的请求方法是 GET。
- health_check 的响应是返回 200 OK 的状态。
- health_check 的响应是个没有本文的信息。
首先, 我们需将 “测试中单元 (Unit Under Test)” 写成一 Rust 的 “Crates”; 因为, 在 Rust 中, 只有将 “测试中单元” 写成是 “Crates”, “测试中单元” 才能被引进 (import) 到集成测试的代码中; 集成测试的代码才能对 “测试中单元” 进行测试。

如图一所示, 测试中单元是位于目录 /src 中的 lib.rs。集成测试的代码是位于目录 /tests 中的 health_check.rs。
Rest API Testing: 测试中单元
测试中单元; /src/lib.rs; 的代码如下:
use actix_web::{web, App, HttpResponse, HttpServer};
use actix_web::dev::Server;
use std::net::TcpListener;
async fn health_check() -> HttpResponse {
HttpResponse::Ok().finish()
}
// Return 'Server' on the happy path
pub fn run(listener: TcpListener) -> Result<Server, std::io::Error> {
let server = HttpServer::new(|| {
App::new()
.route("/health_check", web::get().to(health_check))
})
.listen(listener)?
.run();
// No .await here!
Ok(server)
}
- Server – HttpServer, 主要是要建立起与 API client 端的连接并且启动 Application – App 来处理 Client 的请求。
- 我们以 pub fn run(listener: TcpListener) -> Result<Server, std::io::Error> 来创建一新的 Server – HttpServer; 建立起与 API client 端的连接并且启动 Application – App 来处理 Client 的请求。
- 为了使集成测试的代码; /tests/health_check.rs; 可以调用, 所以, fn run 宣告为 pub。
- 为了确保 Server – HttpServer 在建立连接时, 不会因为 port 上的占用、冲突, 而导致连接上的失败, 所以, 我们将 Server – HttpServe 在建立连接时的 IP 位址, 设为 fn run 的输入参数; listener: TcpListener; 而不是在 fn run 中写入一个固定的 IP 位址。
- App::new().route(“/health_check”,web::get().to(health_check)); “/health_check” 指的是路径。
- web::get().to(health_check); 指的是只有当 http 的请求方法是 GET 时, 才会路由到 async fn health_check() -> HttpResponse; 处理 Client 端的请求。
- async fn health_check() -> HttpResponse; 处理 Client 端的请求; 返回 200 OK 的状态; 响应一个没有本文的信息。
Rest API Testing: 集成测试
集成测试的代码如下; /tests/health_check.rs
use std::net::TcpListener;
#[tokio::test]
async fn health_check_works() {
// Arrange HttpServer::run
let address = spawn_app();
// We need to bring in 'reqwest'
// to perform HTTP requests against our application
let client = reqwest::Client::new();
// Act
let response = client
.get(&format!("{}/health_check", &address))
.send()
.await
.expect("Failed to execute request.");
// Assert
assert!(response.status().is_success());
assert_eq!(Some(0), response.content_length());
}
// Launch our application in the background
// tokio::spawn runs concurrently with down stream futures and tasks; our test logic.
fn spawn_app() -> String {
let address = TcpListener::bind("127.0.0.1:0").expect("Failed to bind random port");
// We retrieve the port assigned to us by the OS
let port = address.local_addr().unwrap().port();
let server = zero2prod::run(address).expect("Failed to bind address");
// Launch the server as a background task
// tokio::spam returns a handle to the spawned future,
// but we have no use for it here, hence the non-binding let
let _ = tokio::spawn(server);
// We return the application address to the caller!
format!("http://127.0.0.1:{}", port)
}
Health check 端点的需求规格是:
- 当我们收到 GET /health_check 的请求时, 我们会返回 200 OK 的状态; 响应一个没有本文的信息。
根据 Health check 端点的需求规格, 集成测试将构建一 Client 的请求; 端点是 /health_check; http 的请求方法是 GET。
// Act
let response = client
.get(&format!("{}/health_check", &address))
.send()
.await
.expect("Failed to execute request.");
fn spawn_app() -> String, 主要是有两个主要的任务:
- 提供 Server – HttpServer 不会因为 port 上的占用、冲突, 而导致连接失败的 IP Address。
let address = TcpListener::bind("127.0.0.1:0").expect("Failed to bind random port");
- 使用 tokio::spawn();将 “/src/lib.rs; 测试中单元 (Unit Under Test); ” 的 pub fn run(listener: TcpListener) -> Result<Server, std::io::Error> 在背景 (background) 中执行; 使得集成测试提出 GET /health_check 的请求与测试中单元 (Unit Under Test) 的运行是可以 “同时发生” 的。

- Assert “返回的状态是 200 OK”
assert!(response.status().is_success());
- Assert “响应的信息是个没有本文的信息”
assert_eq!(Some(0), response.content_length());
结论:
Rust 语言的 Rest API 的集成测试是很容易的; 只需注意:
- 需将 “测试中单元 (Unit Under Test)” 写成一 Rust 的 “Crates”。
- 使用 tokio::spawn(), 使得测试中单元 (Unit Under Test) 在背景 (background) 中执行。
- 确保 Server – HttpServer 在建立连接时, 不会因为 port 上的占用、冲突, 而导致连接上的失败。
期待你加入 Rustaceans; 期待你加入集成测试!
完整的代码, 请参考: https://github.com/KenFang/HealthCheckIntegrationTest