测试
testing.md
commit - 4d8d53cea59bca095ca5c02ef81f0b1791736855 - 2020.09.12
每个应用程序都应当经过良好的测试,actix-web 提供了用于执行单元和集成测试的工具。
单元测试
对于单元测试,actix-web 提供了一个请求 builder 类型。TestRequest 实现了一个类似于 builder 的模式。你可以使用 to_http_request()
生成 HttpRequest
实例,并用它调用 handler 函数。
#[cfg(test)]
mod tests {
use super::*;
use actix_web::test;
#[actix_rt::test]
async fn test_index_ok() {
let req = test::TestRequest::with_header("content-type", "text/plain").to_http_request();
let resp = index(req).await;
assert_eq!(resp.status(), http::StatusCode::OK);
}
#[actix_rt::test]
async fn test_index_not_ok() {
let req = test::TestRequest::default().to_http_request();
let resp = index(req).await;
assert_eq!(resp.status(), http::StatusCode::BAD_REQUEST);
}
}
集成测试
测试应用程序有多种方式。actix-web 可用于在真实 HTTP 服务器上运行具有指定处理程序的应用。
可以使用 TestRequest::get()
、TestRequest::post()
,以及其它方法,向测试服务器发送请求。
要创建用于测试的 Service
,使用 test::init_service
方法,它可以接受常规的 App
构建器。
查阅 API 文档以了解更多信息。
#[cfg(test)]
mod tests {
use super::*;
use actix_web::{test, web, App};
#[actix_rt::test]
async fn test_index_get() {
let mut app = test::init_service(App::new().route("/", web::get().to(index))).await;
let req = test::TestRequest::with_header("content-type", "text/plain").to_request();
let resp = test::call_service(&mut app, req).await;
assert!(resp.status().is_success());
}
#[actix_rt::test]
async fn test_index_post() {
let mut app = test::init_service(App::new().route("/", web::get().to(index))).await;
let req = test::TestRequest::post().uri("/").to_request();
let resp = test::call_service(&mut app, req).await;
assert!(resp.status().is_client_error());
}
}
即使你需要更复杂的应用程序配置,测试也是与创建普通应用程序非常相似的。例如,你可能需要初始化应用程序状态,使用 data
方法创建 App
实例并附加状态,就像在普通应用程序中所做的一样。
#[cfg(test)]
mod tests {
use super::*;
use actix_web::{test, web, App};
#[actix_rt::test]
async fn test_index_get() {
let mut app = test::init_service(
App::new()
.data(AppState { count: 4 })
.route("/", web::get().to(index)),
).await;
let req = test::TestRequest::get().uri("/").to_request();
let resp: AppState = test::read_response_json(&mut app, req).await;
assert_eq!(resp.count, 4);
}
}
流响应测试
如果你需要测试流生成,只需调用 take_body()
并将结果 ResponseBody 转换为 future,然后执行它。例如,下面示例测试服务器发送事件。
use std::task::Poll;
use bytes::Bytes;
use futures::stream::poll_fn;
use actix_web::http::{ContentEncoding, StatusCode};
use actix_web::{web, http, App, Error, HttpRequest, HttpResponse};
async fn sse(_req: HttpRequest) -> HttpResponse {
let mut counter: usize = 5;
// yields `data: N` where N in [5; 1]
let server_events = poll_fn(move |_cx| -> Poll<Option<Result<Bytes, Error>>> {
if counter == 0 {
return Poll::Ready(None);
}
let payload = format!("data: {}\n\n", counter);
counter -= 1;
Poll::Ready(Some(Ok(Bytes::from(payload))))
});
HttpResponse::build(StatusCode::OK)
.set_header(http::header::CONTENT_TYPE, "text/event-stream")
.set_header(
http::header::CONTENT_ENCODING,
ContentEncoding::Identity.as_str(),
)
.streaming(server_events)
}
pub fn main() {
App::new().route("/", web::get().to(sse));
}
#[cfg(test)]
mod tests {
use super::*;
use futures_util::stream::StreamExt;
use futures_util::stream::TryStreamExt;
use actix_web::{test, web, App};
#[actix_rt::test]
async fn test_stream() {
let mut app = test::init_service(App::new().route("/", web::get().to(sse))).await;
let req = test::TestRequest::get().to_request();
let mut resp = test::call_service(&mut app, req).await;
assert!(resp.status().is_success());
// first chunk
let (bytes, mut resp) = resp.take_body().into_future().await;
assert_eq!(bytes.unwrap().unwrap(), Bytes::from_static(b"data: 5\n\n"));
// second chunk
let (bytes, mut resp) = resp.take_body().into_future().await;
assert_eq!(bytes.unwrap().unwrap(), Bytes::from_static(b"data: 4\n\n"));
// remaining part
let bytes = test::load_stream(resp.take_body().into_stream()).await;
assert_eq!(bytes.unwrap(), Bytes::from_static(b"data: 3\n\ndata: 2\n\ndata: 1\n\n"));
}
}