Skip to content

Testing

All your testing, so far, has been manual: you've been launching the application and issuing requests to it with curl. Let's move away from that: it's time to write some automated tests!

Black-box testing

The preferred way to test a Pavex application is to treat it as a black box: you should only test the application through its HTTP interface. This is the most realistic way to test your application: it's how your users will interact with it, after all.

The template project includes a reference example for the /api/ping endpoint:

server/tests/integration/ping.rs
use pavex::http::StatusCode;

//(1)!
use crate::helpers::TestApi;

#[tokio::test]
async fn ping_works() {
    let api = TestApi::spawn().await; //(2)!

    let response = api.get_ping().await; //(3)!

    assert_eq!(response.status(), StatusCode::OK);
}
  1. TestApi is a helper struct that provides a convenient interface to interact with the application. It's defined in server/tests/helpers.rs.
  2. TestApi::spawn starts a new instance of the application in the background.
  3. TestApi::get_ping issues an actual GET /api/ping request to the application.

Add a new integration test

Let's write a new integration test to verify the behaviour on the happy path for GET /api/greet/{name}:

server/tests/integration/main.rs
mod greet;
mod helpers;
mod ping;
server/tests/integration/greet.rs
use pavex::http::StatusCode;

use crate::helpers::TestApi;

#[tokio::test]
async fn greet_happy_path() {
    let api = TestApi::spawn().await;
    let name = "Ursula";

    let response = api
        .api_client
        .get(&format!("{}/api/greet/{name}", &api.api_address))
        .header("User-Agent", "Test runner")
        .send()
        .await
        .expect("Failed to execute request.");

    assert_eq!(response.status(), StatusCode::OK);
    assert_eq!(response.text().await.unwrap(), "Hello, Ursula!");
}

It follows the same pattern as the ping test: it spawns a new instance of the application, issues a request to it and verifies that the response is correct. Let's complement it with a test for the unhappy path as well: requests with a malformed User-Agent header should be rejected.

server/tests/integration/greet.rs
// [...]
#[tokio::test]
async fn non_utf8_user_agent_is_rejected() {
    let api = TestApi::spawn().await;
    let name = "Ursula";

    let response = api
        .api_client
        .get(&format!("{}/api/greet/{name}", &api.api_address))
        .header("User-Agent", b"hello\xfa".as_slice())
        .send()
        .await
        .expect("Failed to execute request.");

    assert_eq!(response.status(), StatusCode::BAD_REQUEST);
    assert_eq!(
        response.text().await.unwrap(),
        "The `User-Agent` header value can only use ASCII printable characters."
    );
}

cargo px test should report three passing tests now. As a bonus exercise, try to add a test for the case where the User-Agent header is missing.