测试 API
集成测试
测试 API 是 CoddyKit 上的免费 Learn Rust Coding 课时。 这是第 4 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 Learn Rust Coding 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 Learn Rust Coding 课程共包含 4 节课。
为什么要测试 API?
测试能让您确信端点的行为正确,并且在修改代码后仍能正常工作。对于 REST API,最有价值的测试是集成测试:它们会端到端地运行真实路由,发送请求并对响应进行断言。
本课将介绍单元测试、Tower 的 oneshot 技巧,以及完整的集成测试。
纯逻辑的单元测试
不接触网络的逻辑(例如验证)可以使用普通的 Rust 单元测试进行测试。将它们放在代码旁边的 #[cfg(test)] 模块中。这些测试通过 cargo test 可以快速运行。
fn validate_title(title: &str) -> bool {
!title.trim().is_empty()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn rejects_empty() {
assert!(!validate_title(" "));
assert!(validate_title("buy milk"));
}
}异步测试
处理程序是异步的,因此需要等待的测试函数必须在运行时上运行。请使用 #[tokio::test],而不是 #[test]。它会为该测试启动 Tokio 运行时,让您可以通过 .await 调用异步代码。
async fn add(a: i32, b: i32) -> i32 { a + b }
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
async fn adds() {
assert_eq!(add(2, 3).await, 5);
}
}不使用网络进行测试
Axum 路由实现了 Tower 的 Service 特征,因此您可以直接向它们传入请求,而无需绑定端口。oneshot 方法接收一个 Request,并返回 Response。这样可以让集成测试快速且具有确定性。
// dev-dependencies needed: tower (for ServiceExt), http-body-util
use axum::{Router, routing::get};
use axum::http::{Request, StatusCode};
use axum::body::Body;
use tower::ServiceExt; // brings in oneshot
async fn check() {
let app = Router::new().route("/health", get(|| async { "ok" }));
let res = app
.oneshot(Request::builder().uri("/health").body(Body::empty()).unwrap())
.await.unwrap();
assert_eq!(res.status(), StatusCode::OK);
}断言状态码
大多数测试首先检查 HTTP 状态。缺少的资源应返回 404,成功创建应返回 201,错误的请求体应返回 400。为该路由构建请求,并将 res.status() 与预期代码进行比较。
use axum::http::{Request, StatusCode};
use axum::body::Body;
use tower::ServiceExt;
async fn missing_returns_404(app: axum::Router) {
let res = app
.oneshot(Request::builder()
.uri("/todos/999")
.body(Body::empty()).unwrap())
.await.unwrap();
assert_eq!(res.status(), StatusCode::NOT_FOUND);
}读取响应体
要对 JSON 进行断言,请将响应体收集为字节,然后将其反序列化。http_body_util::BodyExt::collect 辅助工具会收集请求体,随后 serde_json 将其解析为您的模型,以便对各个字段进行断言。
use http_body_util::BodyExt;
async fn read_json(res: axum::http::Response<axum::body::Body>) {
let bytes = res.into_body().collect().await.unwrap().to_bytes();
let todo: serde_json::Value = serde_json::from_slice(&bytes).unwrap();
assert_eq!(todo["done"], false);
}发送 JSON 请求体
要测试 POST,请使用 JSON 请求体和正确的内容类型标头构建请求。序列化您的输入结构体,设置 content-type: application/json,然后通过 oneshot 传递它。
use axum::http::{Request, StatusCode, header};
use axum::body::Body;
use tower::ServiceExt;
async fn create_returns_201(app: axum::Router) {
let body = serde_json::json!({ "title": "test" }).to_string();
let res = app
.oneshot(Request::builder()
.method("POST").uri("/todos")
.header(header::CONTENT_TYPE, "application/json")
.body(Body::from(body)).unwrap())
.await.unwrap();
assert_eq!(res.status(), StatusCode::CREATED);
}可复用的测试应用构建器
每个测试都应从干净的状态开始。编写一个辅助工具,用新的内存数据库或测试数据库构建全新的路由。每个测试都调用它,可以将测试彼此隔离,使一个测试无法影响另一个测试。
use axum::Router;
use std::sync::{Arc, Mutex};
fn test_app() -> Router {
let store = Arc::new(Mutex::new(Vec::new()));
build(store) // same build() the real server uses
}tests 目录
集成测试位于顶层的 tests/ 文件夹中。其中的每个文件都会作为单独的 crate 编译,并使用您的库的公共 API。这样可以强制您通过真实用户所看到的相同接口进行测试。
tests/api.rs存放您的端点测试。- 使用
cargo test运行全部测试。
// tests/api.rs
use my_api::build_router; // exported from lib.rs
#[tokio::test]
async fn health_ok() {
let _app = build_router();
// send a request and assert ...
}针对数据库进行测试
当处理程序使用 sqlx 时,测试也需要数据库。常见策略包括:专用测试数据库、每次测试结束后回滚的事务,或者使用 sqlx 的 #[sqlx::test] 宏,它会为每个测试自动准备干净的数据库。
// requires sqlx test features and a DATABASE_URL
use sqlx::PgPool;
#[sqlx::test]
async fn inserts_todo(pool: PgPool) {
let todo = create(&pool, "learn rust").await.unwrap();
assert_eq!(todo.title, "learn rust");
assert_eq!(todo.done, false);
}测试哪些内容
目标是构建均衡的测试套件:
- 正常路径:有效请求返回正确的状态和请求体。
- 错误:缺少资源时返回 404,错误输入时返回 400。
- 边界情况:空列表、边界值、重复项。
在持续集成中运行 cargo test,以便在部署前发现回归问题。
快速检查
测试您对 API 测试的理解。
回顾
您学会了如何测试 Rust REST API:
- 对纯逻辑进行单元测试;对异步代码使用
#[tokio::test]。 oneshot可直接驱动路由,无需端口。- 使用请求体和标头构建请求;收集并解析响应体。
- 每个测试使用全新的应用构建器以实现隔离;将集成测试放在
tests/中。 #[sqlx::test]会准备干净的数据库;覆盖正常路径、错误和边界情况。
常见问题解答
「测试 API」课时是免费的吗?
是的 — 「测试 API」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 Learn Rust Coding 课程的其余内容,请升级到 CoddyKit PRO。 Learn Rust Coding 课程共包含 4 节课。
「测试 API」这节课中我会学到什么?
集成测试 你通过在浏览器中直接运行的动手代码来练习 Learn Rust Coding,全天候 AI 导师会在你学习这节课的过程中回答你的问题。
学习 Learn Rust Coding 需要有经验吗?
无需任何先前经验。CoddyKit 上的 Learn Rust Coding 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 4 节课,共 4 节。
「测试 API」课时需要多长时间?
大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。
我能在这节 Learn Rust Coding 课中编写并运行代码吗?
能。每节 Learn Rust Coding 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。