Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Тестирование эндпоинтов

Экосистема Axum предлагает крэйт axum-test, который позволяет проводить полноценное тестирование эндпоинтов, минуя сетевую часть. Это очень удобно, так как позволяет запускать тесты быстрее и не переживать о коллизии портов.

Крэйт axum-test предоставляет обёртку TestServer, при помощи которой можно вызывать эндпоинты “напрямую”:

// Создаём роутер с эндпоинтами, которые будем тестировать
let app = Router::new()
    .route("/give-5", get(|| async { "5" }))

// Создаём тестовый сервер, который работает без сетевого слушателя
let server = TestServer::new(app).unwrap();

// Делаем запрос к эндпоинту "напрямую"
let response = server.get("/give-5").await;

// Проверяем результат запроса
response.assert_text("5");

Давайте добавим тест для нашего hello эндпоинта. При этом мы немного модифицируем код создания роутера, чтобы его было проще тестировать.

Для начала включим axum-test в Cargo.toml.

[package]
name = "test_axum"
version = "0.1.0"
edition = "2024"

[dependencies]
tokio = { version = "1", features = ["full"]}
axum = "0.8"

[dev-dependencies]
axum-test = "18"

Функциональность из крэйта axum-test понадобится только в тестах, поэтому мы объявили её в секции зависимостей для тестов — [dev-dependencies].

Теперь вынесем создание роутера в отдельную функцию, чтобы её можно было вызывать из теста, и допишем сам тест:

use std::collections::HashMap;
use axum::{Router, extract::Query, routing::get};

#[tokio::main]
async fn main() {
    let app = setup_app();
    let listener = tokio::net::TcpListener::bind("0.0.0.0:8080").await.unwrap();
    axum::serve(listener, app).await.unwrap();
}

// Создание роутера
fn setup_app() -> Router {
    Router::new()
        .route("/hello", get(hello))
}

async fn hello(Query(map):Query<HashMap<String, String>>) -> String {
    if let Some(name) = map.get("name") {
        format!("Hello, {name}!")
    } else {
        String::from("Hello!")
    }
}

// Этот модуль, содержащий тест, будет компилироваться только при запуске тестов.
#[cfg(test)]
mod test {
    use axum_test::TestServer;
    use axum::http::StatusCode;
    use super::*;

    #[tokio::test]
    async fn test_hello_endpoint() {
        let app = setup_app();
 
        let server = TestServer::new(app).unwrap();

        // Тестируем вызов без квери параметра
        let response1 = server.get("/hello").await;
        response1.assert_status(StatusCode::OK);
        response1.assert_text("Hello!");

        // Тестируем вызов с квери параметром
        let response2 = server.get("/hello?name=Stas").await;
        response2.assert_status(StatusCode::OK);
        response2.assert_text("Hello, Stas!");
    }
}