Skip to main content
Lesson 1 of 1Intermediate⏱ 15 mins

Integration Testing

Testing routes in memory.

0% of path

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:

code_blocksrust
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.

code_blocksrust
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

Task

Requirements

    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.