Compute アプリケーション向けに Rust でユニットテストを記述する方法
自社のエッジアプリケーションが機能していることを、どのように確認できると思いますか? 私はコードを書いている時はたしかな自信を感じていますが、自分の中で膨らんだ期待が実際にかたちになるとは限りません。自分のコード用にテストを作成するのは、そうした理由からです。
Fastly の Compute は、グローバルに分散された大規模なアプリケーションを構築し、エッジでコードを実行する機能を備えています。今回は、Compute で作業する開発者にローカルテストを提供する Viceroy を活用して、Rustアプリケーションのユニットテストを記述してみます。
アプリケーションの実行
それでは Fastly CLIをインストールし、Rustをインストールしてみましょう。新しいディレクトリに、ルーティングと単純なシンセティックレスポンスを示す新しい Rust スターターキットを準備します :
fastly compute init
Rust を言語として選択し、さらに Default starter for Rust を選択します。
これでディレクトリに作業用アプリケーションが生成されます。コアロジックは src/main.rs で行われます。
このアプリケーションをビルドしてローカルで実行するには以下を実行します :
fastly compute serve
...
INFO: Listening on http://127.0.0.1:7676
http://127.0.0.1:7676 にアクセスすると、ここまでの作業が上手くいったことを示すページが表示されます。
アプリケーションのテスト
コードは機能しているように見えますが、実際に機能しているか検証するためのテストを記述する必要があります。
Viceroy をインストールしてローカルでテストを実行できる状態にした後、cargo-nextest をインストールすると、Wasm インスタンスでテストを実行して結果が集約されます。
プロジェクトの .cargo/config に以下を追加します :
[target.wasm32-wasi]
runner = "viceroy run -C fastly.toml -- "
テストを実行できる状態にするには、src/main.rs の main 関数を書き直す必要があります。以下の通りに変更してください :
fn main() -> Result<(), Error> {
let ds_req = Request::from_client();
let us_resp = handler(ds_req)?;
us_resp.send_to_client();
Ok(())
}
fn handler(req: Request) -> Result<Response, Error> {
...
それでは、fastly クレートを使用して簡単なテストを書いてみましょう。
テストを実施できるエリアは主に3つあります :
予期せぬメソッドを拒否するロジック
シンセティックレスポンスを返すロジック
404ステータスコードを返すロジック
各エリアに関してリクエストを行い、レスポンスのステータスコードやコンテンツタイプ、ボディを検証します。これらの関数をファイルの最後に追加します :
#[test]
fn test_post() {
let req = fastly::Request::post("http://example.com/");
let resp = handler(req).expect("request succeeds");
assert_eq!(resp.get_status(), StatusCode::METHOD_NOT_ALLOWED);
assert_eq!(resp.get_content_type(), Some(mime::TEXT_PLAIN_UTF_8));
assert_eq!(resp.into_body_str(), "This method is not allowed\n");
}
#[test]
fn test_homepage() {
let req = fastly::Request::get("http://example.com/");
let resp = handler(req).expect("request succeeds");
assert_eq!(resp.get_status(), StatusCode::OK);
assert_eq!(resp.get_content_type(), Some(mime::TEXT_HTML_UTF_8));
assert!(resp.into_body_str().contains("Welcome to Compute@Edge"));
}
#[test]
fn test_missing() {
let req = fastly::Request::get("http://example.com/missing");
let resp = handler(req).expect("request succeeds");
assert_eq!(resp.get_status(), StatusCode::OK);
assert_eq!(resp.get_content_type(), Some(mime::TEXT_PLAIN_UTF_8));
assert_eq!(
resp.into_body_str(),
"The page you requested could not be found\n"
);
}
cargo nextest run でテストを実行できます。巧妙なバグがハンドラに入り込んだ場合、404ステータスコードが表示されるはずが200ステータスコードを受け取るという、以下のようなエラーが発生する可能性があります。
cargo nextest run
...
thread 'main' panicked at 'assertion failed: `(left == right)`
left: `404`,
right: `200`', src/main.rs:122:5
Canceling due to test failure: 0 tests still running
------------
Summary [ 3.125s] 5 tests run: 4 passed, 1 failed, 0 skipped
FAIL [ 3.122s] fastly-compute-project::bin/fastly-compute-project test_missing
error: test run failed
しかし今回はハンドラにバグがまったく無く、予想していた通りに機能しています。ポジティブなテスト結果を得られました :
cargo nextest run
...
Starting 3 tests across 1 binary
PASS [ 1.596s] fastly-compute-project::bin/fastly-compute-project test_post
PASS [ 1.885s] fastly-compute-project::bin/fastly-compute-project test_homepage
PASS [ 1.900s] fastly-compute-project::bin/fastly-compute-project test_missing
------------
Summary [ 1.902s] 3 tests run: 3 passed, 0 skipped
さまざまな検証方法
この記事では、Rust Compute アプリケーションのユニットテスト方法の詳細をご紹介しました。今回紹介した以外にもさまざまな方法があります。例えば複雑なロジックの場合は、検証可能な小さな関数に分割することをお勧めします。さらに、より一歩先に進みたい方には、関数自体を書く前にテストを記述するという方法もあります。ぜひ実際に試してみてください! Fastly のサーバーレスプラットフォームをまだご利用でないお客様は、ぜひ無料トライアルで Compute を実際にお試しください。