考慮到我們的 Web Server 有多個功能,例如產生 QR Code、驗證顏色碼、從地址獲取經緯度等,整合測試將確保所有這些部分能夠正確地一起工作。
我們的 index() 和 generate_svg() 分別負責處理 HTTP GET 和 POST 請求。這些函式調用了多個其他函式,如 get_code_data() 和 get_coordinates(),因此它們是整合測試的理想目標。
我們可以使用 actix_web::test 來模擬 HTTP 請求。
#[cfg(test)]
mod integration_tests {
    use super::*;
    use actix_web::http::StatusCode;
    use actix_web::{test, App};
    use serde_json::json;
    #[actix_rt::test]
    async fn test_index() {
        let mut app = test::init_service(
            App::new().service(index),
        )
        .await;
        let req = test::TestRequest::get()
            .uri("/generate_qr?email=bucky0112@gmail.com")
            .to_request();
        let resp = test::call_service(&mut app, req).await;
        assert_eq!(resp.status(), StatusCode::OK);
    }
    #[actix_rt::test]
    async fn test_generate_svg_function() {
        let mut app = test::init_service(
            App::new().service(generate_svg),
        )
        .await;
        let payload = json!({
            "url": "https://example.com",
        });
        let req = test::TestRequest::post()
            .uri("/generate_qr_svg")
            .set_json(&payload)
            .to_request();
        let resp = test::call_service(&mut app, req).await;
        assert_eq!(resp.status(), StatusCode::OK);
    }
}
然後還需要在 models/mod.rs 加上:
#[derive(serde::Deserialize, serde::Serialize)]
pub struct Info {
    pub url: Option<String>,
    pub phone: Option<String>,
    pub email: Option<String>,
    pub address: Option<String>,
    pub foreground: Option<String>,
    pub background: Option<String>,
    pub dimension: Option<u32>,
}
接著跑測試 cargo test
出來的結果是:
❯ cargo test
   Compiling qrcode-actix v0.1.0 (/Users/buckychu/sideProjects/qrcode-actix)
    Finished test [unoptimized + debuginfo] target(s) in 2.79s
     Running unittests src/main.rs (target/debug/deps/qrcode_actix-f5c31d729f1fd710)
running 5 tests
test api::tests::test_get_coordinates ... ok
test api::tests::test_get_code_data ... ok
test api::tests::test_is_valid_color ... ok
test api::integration_tests::test_index ... FAILED
test api::integration_tests::test_generate_svg_function ... ok
failures:
---- api::integration_tests::test_index stdout ----
thread 'api::integration_tests::test_index' panicked at 'assertion failed: `(left == right)`
  left: `400`,
 right: `200`', src/api/mod.rs:267:9
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
failures:
    api::integration_tests::test_index
test result: FAILED. 4 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.01s
根據測試結果,test_index 測試失敗了,因為它期望得到的 HTTP 狀態碼是 200 OK,但實際得到的是 400 Bad Request。看起來我們要測試 email 但是失敗了,因為我們之前似乎都忘了在 index() 加入新的參數。
所以我們先更改一下測試:
#[cfg(test)]
mod integration_tests {
    use super::*;
    use actix_web::http::StatusCode;
    use actix_web::{test, App};
    use serde_json::json;
    #[actix_rt::test]
    async fn test_index() {
        let mut app = test::init_service(App::new().service(index)).await;
        let req = test::TestRequest::get()
            .uri("/generate_qr?url=https://example.com")
            .to_request();
        let resp = test::call_service(&mut app, req).await;
        assert_eq!(resp.status(), StatusCode::OK);
    }
}
這樣測試結果沒問題,全部的測試都通過。🎉
❯ cargo test
   Compiling qrcode-actix v0.1.0 (/Users/buckychu/sideProjects/qrcode-actix)
    Finished test [unoptimized + debuginfo] target(s) in 2.70s
     Running unittests src/main.rs (target/debug/deps/qrcode_actix-f5c31d729f1fd710)
running 5 tests
test api::tests::test_get_code_data ... ok
test api::tests::test_get_coordinates ... ok
test api::tests::test_is_valid_color ... ok
test api::integration_tests::test_generate_svg_function ... ok
test api::integration_tests::test_index ... ok
test result: ok. 5 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.02s
然後原本的 index() 也做一下修正,可以加入其他的參數,例如 email。
#[get("/generate_qr")]
async fn index(data: web::Query<Info>) -> HttpResponse {
    let code_data = match get_code_data(&data).await {
        Some(data) => data,
        None => return HttpResponse::BadRequest().body("缺少有效數據"),
    };
    let code = match QrCode::new(code_data) {
        Ok(c) => c,
        Err(_) => return HttpResponse::BadRequest().body("你輸入的字串無法處理"),
    };
    let image = code.render::<Luma<u8>>().build();
    let mut buffer = Vec::new();
    match DynamicImage::ImageLuma8(image).write_to(&mut buffer, image::ImageOutputFormat::Png) {
        Ok(_) => (),
        Err(_) => return HttpResponse::InternalServerError().body("無法生成圖像"),
    }
    HttpResponse::Ok().content_type("image/png").body(buffer)
}
現在 index() 也可以產生其他的 QR code 了,明天我們持續做好測試的部分。
