Markdown Files
Actix-Web Integration Testing
This project uses actix-web with the actix_web::test module for integration tests. All integration tests live in #[cfg(test)] mod tests blocks within the source files they test.
Core Pattern
Use test::init_service to spin up an in-memory App with your routes, then send requests with TestRequest:
#[cfg(test)]
mod tests {
use actix_web::{http::StatusCode, test, App};
use super::*;
#[actix_web::test]
async fn test_up_get() {
let app = test::init_service(App::new().service(up)).await;
let req = test::TestRequest::get().uri("/up").to_request();
let resp = test::call_service(&app, req).await;
assert_eq!(resp.status(), StatusCode::OK);
}
}
Key Helpers
| Helper | Purpose |
|---|---|
test::init_service(app) | Build a test Service from an App |
test::TestRequest::get() | Build a GET request |
test::TestRequest::post() | Build a POST request |
test::TestRequest::default() | Build a request you customize (method, URI, headers, body) |
.uri("/path") | Set the request path |
.insert_header(ContentType::plaintext()) | Add a header |
.set_json(payload) | Set a JSON body (serializes automatically) |
.to_request() | Finalize the TestRequest into a Request |
.to_http_request() | Finalize into an HttpRequest (for calling handlers directly) |
test::call_service(&app, req) | Send a request through the service and get a ServiceResponse |
test::call_and_read_body(&app, req) | Call service and read the body as Bytes |
test::call_and_read_body_json(&app, req) | Call service and deserialize the JSON body |
Testing Responses
Check status codes:
let resp = test::call_service(&app, req).await;
assert!(resp.status().is_success());
assert_eq!(resp.status(), StatusCode::OK);
assert!(resp.status().is_client_error());
Assert body content:
let body = test::call_and_read_body(&app, req).await;
assert_eq!(&body, b"expected body");
Assert JSON responses:
let result: MyStruct = test::call_and_read_body_json(&app, req).await;
assert_eq!(result.field, expected_value);
Testing Handlers Directly (Unit Tests)
For standalone handlers (not using routing macros), call them with a HttpRequest built from TestRequest:
#[actix_web::test]
async fn test_handler_unit() {
let req = test::TestRequest::default()
.insert_header(ContentType::plaintext())
.to_http_request();
let resp = my_handler(req).await;
assert_eq!(resp.status(), StatusCode::OK);
}
Testing with App State
If your app uses shared state via web::Data, include it in init_service:
#[actix_web::test]
async fn test_with_state() {
let state = web::Data::new(AppState { count: 4 });
let app = test::init_service(
App::new()
.app_data(state)
.service(index),
)
.await;
let resp = test::call_service(&app, test::TestRequest::get().uri("/").to_request()).await;
assert!(resp.status().is_success());
}
Running Tests
just test # cargo test
just verify # fmt → check → clippy → test