'How to get the body of a Response in actix_web unit test?
I'm building a web API service with Rust and actix_web.
I want to test a route and check if the received response body is what I expect. But I'm struggling with converting the received body ResponseBody<Body>
into JSON or BSON. The called route actually returns 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.
Solution 1:[1]
Having a look at Body and ResponseBody, this looks like the approach:
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
Solution 2:[2]
The actix/examples repository achieves this by defining a new trait called BodyTest...
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!(),
},
}
}
}
After which you may simply do:
assert_eq!(resp.response().body().as_str(), "Your name is John");
Actix Web 4
This trait is now much simpler (you could skip entirely):
trait BodyTest {
fn as_str(&self) -> &str;
}
impl BodyTest for Bytes {
fn as_str(&self) -> &str {
std::str::from_utf8(self).unwrap()
}
}
And to use it:
let body = to_bytes(resp.into_body()).await.unwrap();
assert_eq!(body.as_str(), "Your name is John");
Reference to full code these excerpts were taken from: https://github.com/actix/examples/blob/master/forms/form/src/main.rs
Solution 3:[3]
This works for testing in one go, the body and status code :
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);
Solution 4:[4]
#[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"));
}
Please see the doc
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | Njuguna Mureithi |
Solution 2 | |
Solution 3 | Krishna |
Solution 4 | Mike Shauneu |