Integration Testing
Testing routes in memory.
Learning Objectives
- Use test::call_service
##Writing Integration Tests for Actix Web
Testing is a critical part of building robust APIs. Actix Web provides excellent support for integration testing right out of the box using the actix_web::test module. Instead of spinning up a real TCP server and making HTTP requests over the network, Actix Web allows you to run your application purely in memory. This makes tests incredibly fast and reliable.
#The `actix_web::test` module
To test an Actix Web service, you use test::init_service to instantiate your App inside the test environment. You can then use test::TestRequest to construct mock HTTP requests and test::call_service to send those requests to your app and get the response.
Let's look at a simple example:
use actix_web::{get, web, App, Responder, HttpResponse};
#[get("/hello")]
async fn hello() -> impl Responder {
HttpResponse::Ok().body("Hello, world!")
}
#[cfg(test)]
mod tests {
use super::*;
use actix_web::{test, App};
#[actix_web::test]
async fn test_hello_get() {
// 1. Initialize the app in memory
let app = test::init_service(App::new().service(hello)).await;
// 2. Create a mock request
let req = test::TestRequest::get().uri("/hello").to_request();
// 3. Call the service
let resp = test::call_service(&app, req).await;
// 4. Assert the response
assert!(resp.status().is_success());
}
}#Testing JSON Responses
When testing endpoints that return JSON, you can use test::read_body_json or test::read_body to extract and deserialize the payload.
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, Debug, PartialEq)]
struct User {
id: u32,
name: String,
}
#[actix_web::test]
async fn test_json_response() {
// ... initialize app ...
let req = test::TestRequest::get().uri("/user").to_request();
let resp = test::call_service(&app, req).await;
assert!(resp.status().is_success());
// Read and deserialize the body
let body: User = test::read_body_json(resp).await;
assert_eq!(body.name, "Alice");
}By testing your endpoints this way, you ensure your routing, extractors, and responders work correctly together, without the overhead of network calls.
Practical Exercise
Write an Integration Test
TaskRequirements
Try solving the exercise in your local environment first. Once you have a solution, or if you get completely stuck, reveal the answer.
Key Takeaways
- Use `#[actix_web::test]` instead of `#[tokio::test]` for your test functions.
- `test::init_service` compiles your `App` into a testable in-memory service.
- `TestRequest` allows you to construct requests with custom headers, JSON bodies, and query params.
- Use `test::call_service` to send the request and get the response back.
- `test::read_body_json` makes it easy to assert against typed JSON responses.
Finished reading?
Mark this lesson as done to track your progress and earn XP.