sontixyou blog

技術まわり、ガジェット関連について

axumで作ったAPIのテストを書く際にハマった話

背景

2024/6あたりにRustでWebアプリ作れたら、面白いかもと思い、フレームワークなんなりを探していた。

そんな中で、axum(アクサム)を見つけた。Youtubeの外国人が、アクサムと言っていたので、たぶん日本語読みはアクサムが正しい。

github.com

このaxumを使って、APIを試しに作った。そのときに、実装コードを書くだけではつまらないので、テストコードも書くことにした。

しかし、テストコードを書くときに2〜3時間くらい、どんな感じでテストを書けば良いのかわからず、ハマったので記録として残しておく。

問題

普段、私はGitHubを使って、コード管理している。

そのため、ローカル環境とGitHub ActionsのCI両方で、テストをパスする必要が出てくる。

ひよっこRustaceanのわたしは、次のようなテストコードを書いていた。

このテストコードを実行すると、ローカル環境ではパスするが、GitHub Actions上ではパスしない。

GitHub Actions上でサーバーを起動しっぱなしになるようで、テストの実行が完了するが、サーバーが起動しつづけているので、ワークフローが完了しない。

// main.rs
// 良くない例

use axum::{
    routing::get,
    Router,
};

#[tokio::main]
async fn main() {
    // build our application with a single route
    let app = Router::new().route("/", get(|| async { "Hello, World!" }));

    // run our app with hyper, listening globally on port 3000
    let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap();
    axum::serve(listener, app).await.unwrap();
}

mod tests {
    use reqwest::StatusCode;
    use std::process::Command;

    #[test]
    fn test_hello_world() {
        Command::new("cargo").arg("run").output().unwrap();

        let client = reqwest::blocking::Client::new();
        let response = client.get("http://0.0.0.0:3000/").send().unwrap();

        assert_eq!(response.status(), StatusCode::OK);
        let text = response.text().unwrap();
        assert_eq!(text, "Hello, World!");
    }
}

解決

axum-testというcrateを追加することで、axumのモックサーバーを使うことができる。

github.com

axumのリポジトリには、テスト周りの記述がなく、このcrateを探すまでにとても苦労した。

また、axum-testについて紹介しているブログ記事等も少なく感じたので、紹介することにした。