mirror of
https://github.com/actix/actix-web.git
synced 2024-11-22 17:41:11 +00:00
Add fallible versions of test_utils helpers to actix-test (#2961)
Co-authored-by: Rob Ede <robjtede@icloud.com>
This commit is contained in:
parent
b9f54c8796
commit
6627109984
3 changed files with 102 additions and 12 deletions
|
@ -7,6 +7,7 @@
|
|||
- Add `Logger::custom_response_replace()`. [#2631]
|
||||
- Add rudimentary redirection service at `web::redirect()` / `web::Redirect`. [#1961]
|
||||
- Add `guard::Acceptable` for matching against `Accept` header mime types. [#2265]
|
||||
- Add fallible versions of test helpers: `try_call_service`, `try_call_and_read_body_json`, `try_read_body`, and `try_read_body_json`. [#2961]
|
||||
|
||||
### Fixed
|
||||
- Add `Allow` header to `Resource`'s default responses when no routes are matched. [#2949]
|
||||
|
@ -17,7 +18,7 @@
|
|||
[#2784]: https://github.com/actix/actix-web/pull/2784
|
||||
[#2867]: https://github.com/actix/actix-web/pull/2867
|
||||
[#2949]: https://github.com/actix/actix-web/pull/2949
|
||||
|
||||
[#2961]: https://github.com/actix/actix-web/pull/2961
|
||||
|
||||
## 4.2.1 - 2022-09-12
|
||||
### Fixed
|
||||
|
|
|
@ -10,12 +10,16 @@
|
|||
//! # Calling Test Service
|
||||
//! - [`TestRequest`]
|
||||
//! - [`call_service`]
|
||||
//! - [`try_call_service`]
|
||||
//! - [`call_and_read_body`]
|
||||
//! - [`call_and_read_body_json`]
|
||||
//! - [`try_call_and_read_body_json`]
|
||||
//!
|
||||
//! # Reading Response Payloads
|
||||
//! - [`read_body`]
|
||||
//! - [`try_read_body`]
|
||||
//! - [`read_body_json`]
|
||||
//! - [`try_read_body_json`]
|
||||
|
||||
// TODO: more docs on generally how testing works with these parts
|
||||
|
||||
|
@ -31,7 +35,8 @@ pub use self::test_services::{default_service, ok_service, simple_service, statu
|
|||
#[allow(deprecated)]
|
||||
pub use self::test_utils::{
|
||||
call_and_read_body, call_and_read_body_json, call_service, init_service, read_body,
|
||||
read_body_json, read_response, read_response_json,
|
||||
read_body_json, read_response, read_response_json, try_call_and_read_body_json,
|
||||
try_call_service, try_read_body, try_read_body_json,
|
||||
};
|
||||
|
||||
#[cfg(test)]
|
||||
|
|
|
@ -100,6 +100,15 @@ where
|
|||
.expect("test service call returned error")
|
||||
}
|
||||
|
||||
/// Fallible version of [`call_service`] that allows testing response completion errors.
|
||||
pub async fn try_call_service<S, R, B, E>(app: &S, req: R) -> Result<S::Response, E>
|
||||
where
|
||||
S: Service<R, Response = ServiceResponse<B>, Error = E>,
|
||||
E: std::fmt::Debug,
|
||||
{
|
||||
app.call(req).await
|
||||
}
|
||||
|
||||
/// Helper function that returns a response body of a TestRequest
|
||||
///
|
||||
/// # Examples
|
||||
|
@ -185,13 +194,23 @@ pub async fn read_body<B>(res: ServiceResponse<B>) -> Bytes
|
|||
where
|
||||
B: MessageBody,
|
||||
{
|
||||
let body = res.into_body();
|
||||
body::to_bytes(body)
|
||||
try_read_body(res)
|
||||
.await
|
||||
.map_err(Into::<Box<dyn StdError>>::into)
|
||||
.expect("error reading test response body")
|
||||
}
|
||||
|
||||
/// Fallible version of [`read_body`] that allows testing MessageBody reading errors.
|
||||
pub async fn try_read_body<B>(
|
||||
res: ServiceResponse<B>,
|
||||
) -> Result<Bytes, <B as MessageBody>::Error>
|
||||
where
|
||||
B: MessageBody,
|
||||
{
|
||||
let body = res.into_body();
|
||||
body::to_bytes(body).await
|
||||
}
|
||||
|
||||
/// Helper function that returns a deserialized response body of a ServiceResponse.
|
||||
///
|
||||
/// # Examples
|
||||
|
@ -240,18 +259,27 @@ where
|
|||
B: MessageBody,
|
||||
T: DeserializeOwned,
|
||||
{
|
||||
let body = read_body(res).await;
|
||||
|
||||
serde_json::from_slice(&body).unwrap_or_else(|err| {
|
||||
try_read_body_json(res).await.unwrap_or_else(|err| {
|
||||
panic!(
|
||||
"could not deserialize body into a {}\nerr: {}\nbody: {:?}",
|
||||
"could not deserialize body into a {}\nerr: {}",
|
||||
std::any::type_name::<T>(),
|
||||
err,
|
||||
body,
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
/// Fallible version of [`read_body_json`] that allows testing response deserialzation errors.
|
||||
pub async fn try_read_body_json<T, B>(res: ServiceResponse<B>) -> Result<T, Box<dyn StdError>>
|
||||
where
|
||||
B: MessageBody,
|
||||
T: DeserializeOwned,
|
||||
{
|
||||
let body = try_read_body(res)
|
||||
.await
|
||||
.map_err(Into::<Box<dyn StdError>>::into)?;
|
||||
serde_json::from_slice(&body).map_err(Into::<Box<dyn StdError>>::into)
|
||||
}
|
||||
|
||||
/// Helper function that returns a deserialized response body of a TestRequest
|
||||
///
|
||||
/// # Examples
|
||||
|
@ -299,8 +327,23 @@ where
|
|||
B: MessageBody,
|
||||
T: DeserializeOwned,
|
||||
{
|
||||
let res = call_service(app, req).await;
|
||||
read_body_json(res).await
|
||||
try_call_and_read_body_json(app, req).await.unwrap()
|
||||
}
|
||||
|
||||
/// Fallible version of [`call_and_read_body_json`] that allows testing service call errors.
|
||||
pub async fn try_call_and_read_body_json<S, B, T>(
|
||||
app: &S,
|
||||
req: Request,
|
||||
) -> Result<T, Box<dyn StdError>>
|
||||
where
|
||||
S: Service<Request, Response = ServiceResponse<B>, Error = Error>,
|
||||
B: MessageBody,
|
||||
T: DeserializeOwned,
|
||||
{
|
||||
let res = try_call_service(app, req)
|
||||
.await
|
||||
.map_err(Into::<Box<dyn StdError>>::into)?;
|
||||
try_read_body_json(res).await
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
|
@ -358,7 +401,7 @@ mod tests {
|
|||
assert_eq!(result, Bytes::from_static(b"delete!"));
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct Person {
|
||||
id: String,
|
||||
name: String,
|
||||
|
@ -383,6 +426,26 @@ mod tests {
|
|||
assert_eq!(&result.id, "12345");
|
||||
}
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn test_try_response_json_error() {
|
||||
let app = init_service(App::new().service(web::resource("/people").route(
|
||||
web::post().to(|person: web::Json<Person>| HttpResponse::Ok().json(person)),
|
||||
)))
|
||||
.await;
|
||||
|
||||
let payload = r#"{"id":"12345","name":"User name"}"#.as_bytes();
|
||||
|
||||
let req = TestRequest::post()
|
||||
.uri("/animals") // Not registered to ensure an error occurs.
|
||||
.insert_header((header::CONTENT_TYPE, "application/json"))
|
||||
.set_payload(payload)
|
||||
.to_request();
|
||||
|
||||
let result: Result<Person, Box<dyn StdError>> =
|
||||
try_call_and_read_body_json(&app, req).await;
|
||||
assert!(result.is_err());
|
||||
}
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn test_body_json() {
|
||||
let app = init_service(App::new().service(web::resource("/people").route(
|
||||
|
@ -403,6 +466,27 @@ mod tests {
|
|||
assert_eq!(&result.name, "User name");
|
||||
}
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn test_try_body_json_error() {
|
||||
let app = init_service(App::new().service(web::resource("/people").route(
|
||||
web::post().to(|person: web::Json<Person>| HttpResponse::Ok().json(person)),
|
||||
)))
|
||||
.await;
|
||||
|
||||
// Use a number for id to cause a deserialization error.
|
||||
let payload = r#"{"id":12345,"name":"User name"}"#.as_bytes();
|
||||
|
||||
let res = TestRequest::post()
|
||||
.uri("/people")
|
||||
.insert_header((header::CONTENT_TYPE, "application/json"))
|
||||
.set_payload(payload)
|
||||
.send_request(&app)
|
||||
.await;
|
||||
|
||||
let result: Result<Person, Box<dyn StdError>> = try_read_body_json(res).await;
|
||||
assert!(result.is_err());
|
||||
}
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn test_request_response_form() {
|
||||
let app = init_service(App::new().service(web::resource("/people").route(
|
||||
|
|
Loading…
Reference in a new issue