rust 如何在actix_web单元测试中获取响应的主体?

9o685dep  于 2023-10-20  发布在  其他
关注(0)|答案(4)|浏览(194)

我正在使用Rust和actix_web构建一个Web API服务。
我想测试一个路由并检查接收到的响应体是否是我所期望的。但是我正在努力将收到的body ResponseBody<Body>转换为JSON或BSON。被调用的路由实际上返回application/json

let mut app = test::init_service(App::new()
        .data(AppState { database: db.clone() })
        .route("/recipes/{id}", web::post().to(add_one_recipe))
    ).await;

let payload = create_one_recipe().as_document().unwrap().clone();

let req = test::TestRequest::post()
    .set_json(&payload).uri("/recipes/new").to_request();

let mut resp = test::call_service(&mut app, req).await;
let body: ResponseBody<Body> = resp.take_body(); // Here I want the body as Binary, String, JSON, or BSON. The response is actually application/json.
i86rm4rw

i86rm4rw1#

actix/examples仓库通过定义一个名为BodyTest的新trait来实现这一点。

Actix Web 3

trait BodyTest {
    fn as_str(&self) -> &str;
}

impl BodyTest for ResponseBody<Body> {
    fn as_str(&self) -> &str {
        match self {
            ResponseBody::Body(ref b) => match b {
                Body::Bytes(ref by) => std::str::from_utf8(&by).unwrap(),
                _ => panic!(),
            },
            ResponseBody::Other(ref b) => match b {
                Body::Bytes(ref by) => std::str::from_utf8(&by).unwrap(),
                _ => panic!(),
            },
        }
    }
}

之后,你可以简单地做:

assert_eq!(resp.response().body().as_str(), "Your name is John");

Actix Web 4

这个特性现在简单多了(你可以完全跳过):

trait BodyTest {
    fn as_str(&self) -> &str;
}

impl BodyTest for Bytes {
    fn as_str(&self) -> &str {
        std::str::from_utf8(self).unwrap()
    }
}

并使用它:

let body = to_bytes(resp.into_body()).await.unwrap();
assert_eq!(body.as_str(), "Your name is John");

参考完整代码,这些摘录摘自:https://github.com/actix/examples/blob/master/forms/form/src/main.rs

ma8fv8wu

ma8fv8wu2#

看看Body和ResponseBody,这看起来像是一种方法:

use actix_web::{web, App, HttpResponse, HttpServer, Responder};
use serde::{Deserialize, Serialize};

#[derive(Serialize, Deserialize)]
struct Greet {
    name: String,
}

async fn greet() -> impl Responder {
    let body = serde_json::to_string(&Greet {
        name: "Test".to_owned(),
    })
    .unwrap();
    HttpResponse::Ok()
        .content_type("application/json")
        .body(body)
}

#[actix_rt::main]
async fn main() -> std::io::Result<()> {
    HttpServer::new(|| App::new().route("/", web::get().to(greet)))
        .bind("127.0.0.1:8000")?
        .run()
        .await
}

#[cfg(test)]
mod tests {
    use super::*;
    use actix_web::{body::Body, test, web, App};
    use serde_json::json;

    #[actix_rt::test]
    async fn test_greet_get() {
        let mut app = test::init_service(App::new().route("/", web::get().to(greet))).await;
        let req = test::TestRequest::with_header("content-type", "application/json").to_request();
        let mut resp = test::call_service(&mut app, req).await;
        let body = resp.take_body();
        let body = body.as_ref().unwrap();
        assert!(resp.status().is_success());
        assert_eq!(
            &Body::from(json!({"name":"Test"})), // or serde.....
            body
        );
    }
}
running 1 test
test tests::test_greet_get ... ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out
pn9klfpd

pn9klfpd3#

这适用于一次性测试,主体和状态代码:

let req = test::TestRequest::get()
    .uri(&format!("/brand/{}", 999))
    .to_request();
let resp = test::call_service(&mut app, req).await;
assert_eq!(resp.status(), StatusCode::NOT_FOUND);
let body = test::read_body(resp).await;
assert_eq!(actix_web::web::Bytes::from("Item not found"), body);
ni65a41a

ni65a41a4#

#[actix_rt::test]
pub async fn test_index() {
    let mut app = test::init_service(App::new().service(ping)).await;
    let req = test::TestRequest::get().uri("/ping").to_request();
    let result = test::read_response(&mut app, req).await;
    assert_eq!(result, Bytes::from_static(b"PONG"));
}

请参阅doc

相关问题