Testing - Integration Tests

1. Welcome and Icebreaker:

Have you ever found a bug that only showed up when multiple parts of your app worked together?

2. Outline the Session:

Agenda:

  1. Concept Explanation: What are integration tests and why are they important?
  2. Live Coding: Write in-process integration tests for Axum routes using tower::ServiceExt.
  3. Tasks: Practice by testing your own endpoints.

Learning Outcomes:

  • Understand the purpose of integration tests in Axum projects.
  • Write and run integration tests for API endpoints.

Expectations: Try testing both successful and failing requests!

3. Concept Explanation:

Integration tests check how different parts of your app work together. For a router test without a separately running server, use tower::ServiceExt::oneshot. A network-level test can use reqwest with a listener bound to port 0 and a server spawned by the test.

4. Live Coding Demonstration:

Add tower = { version = "0.5", features = ["util"] } alongside Axum 0.8 and Tokio 1. Put the test below in src/main.rs (or a library module) so it can call the root handler from Day 1:

#[cfg(test)]
mod tests {
    use super::*;
    use axum::{body::Body, http::{Request, StatusCode}, routing::get, Router};
    use tower::ServiceExt;

    #[tokio::test]
    async fn test_get_root() {
        let app = Router::new().route("/", get(root));
        let resp = app.oneshot(Request::builder().uri("/").body(Body::empty()).unwrap())
            .await.unwrap();
        assert_eq!(resp.status(), StatusCode::OK);
    }
}

Best Practices:

  • Use a separate test database if needed.
  • Clean up test data between runs.

Common Pitfalls:

  • Not isolating tests from production data.
  • Assuming a server exists at a fixed port; oneshot does not require one.

5. Tasks

  • Write an integration test for a POST endpoint.
  • Test error responses and edge cases.
  • Try collecting the response body with http-body-util and checking its contents.