mirror of
https://github.com/actix/actix-web.git
synced 2024-11-29 21:11:17 +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 `Logger::custom_response_replace()`. [#2631]
|
||||||
- Add rudimentary redirection service at `web::redirect()` / `web::Redirect`. [#1961]
|
- Add rudimentary redirection service at `web::redirect()` / `web::Redirect`. [#1961]
|
||||||
- Add `guard::Acceptable` for matching against `Accept` header mime types. [#2265]
|
- 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
|
### Fixed
|
||||||
- Add `Allow` header to `Resource`'s default responses when no routes are matched. [#2949]
|
- 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
|
[#2784]: https://github.com/actix/actix-web/pull/2784
|
||||||
[#2867]: https://github.com/actix/actix-web/pull/2867
|
[#2867]: https://github.com/actix/actix-web/pull/2867
|
||||||
[#2949]: https://github.com/actix/actix-web/pull/2949
|
[#2949]: https://github.com/actix/actix-web/pull/2949
|
||||||
|
[#2961]: https://github.com/actix/actix-web/pull/2961
|
||||||
|
|
||||||
## 4.2.1 - 2022-09-12
|
## 4.2.1 - 2022-09-12
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
|
@ -10,12 +10,16 @@
|
||||||
//! # Calling Test Service
|
//! # Calling Test Service
|
||||||
//! - [`TestRequest`]
|
//! - [`TestRequest`]
|
||||||
//! - [`call_service`]
|
//! - [`call_service`]
|
||||||
|
//! - [`try_call_service`]
|
||||||
//! - [`call_and_read_body`]
|
//! - [`call_and_read_body`]
|
||||||
//! - [`call_and_read_body_json`]
|
//! - [`call_and_read_body_json`]
|
||||||
|
//! - [`try_call_and_read_body_json`]
|
||||||
//!
|
//!
|
||||||
//! # Reading Response Payloads
|
//! # Reading Response Payloads
|
||||||
//! - [`read_body`]
|
//! - [`read_body`]
|
||||||
|
//! - [`try_read_body`]
|
||||||
//! - [`read_body_json`]
|
//! - [`read_body_json`]
|
||||||
|
//! - [`try_read_body_json`]
|
||||||
|
|
||||||
// TODO: more docs on generally how testing works with these parts
|
// 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)]
|
#[allow(deprecated)]
|
||||||
pub use self::test_utils::{
|
pub use self::test_utils::{
|
||||||
call_and_read_body, call_and_read_body_json, call_service, init_service, read_body,
|
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)]
|
#[cfg(test)]
|
||||||
|
|
|
@ -100,6 +100,15 @@ where
|
||||||
.expect("test service call returned error")
|
.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
|
/// Helper function that returns a response body of a TestRequest
|
||||||
///
|
///
|
||||||
/// # Examples
|
/// # Examples
|
||||||
|
@ -185,13 +194,23 @@ pub async fn read_body<B>(res: ServiceResponse<B>) -> Bytes
|
||||||
where
|
where
|
||||||
B: MessageBody,
|
B: MessageBody,
|
||||||
{
|
{
|
||||||
let body = res.into_body();
|
try_read_body(res)
|
||||||
body::to_bytes(body)
|
|
||||||
.await
|
.await
|
||||||
.map_err(Into::<Box<dyn StdError>>::into)
|
.map_err(Into::<Box<dyn StdError>>::into)
|
||||||
.expect("error reading test response body")
|
.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.
|
/// Helper function that returns a deserialized response body of a ServiceResponse.
|
||||||
///
|
///
|
||||||
/// # Examples
|
/// # Examples
|
||||||
|
@ -240,18 +259,27 @@ where
|
||||||
B: MessageBody,
|
B: MessageBody,
|
||||||
T: DeserializeOwned,
|
T: DeserializeOwned,
|
||||||
{
|
{
|
||||||
let body = read_body(res).await;
|
try_read_body_json(res).await.unwrap_or_else(|err| {
|
||||||
|
|
||||||
serde_json::from_slice(&body).unwrap_or_else(|err| {
|
|
||||||
panic!(
|
panic!(
|
||||||
"could not deserialize body into a {}\nerr: {}\nbody: {:?}",
|
"could not deserialize body into a {}\nerr: {}",
|
||||||
std::any::type_name::<T>(),
|
std::any::type_name::<T>(),
|
||||||
err,
|
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
|
/// Helper function that returns a deserialized response body of a TestRequest
|
||||||
///
|
///
|
||||||
/// # Examples
|
/// # Examples
|
||||||
|
@ -299,8 +327,23 @@ where
|
||||||
B: MessageBody,
|
B: MessageBody,
|
||||||
T: DeserializeOwned,
|
T: DeserializeOwned,
|
||||||
{
|
{
|
||||||
let res = call_service(app, req).await;
|
try_call_and_read_body_json(app, req).await.unwrap()
|
||||||
read_body_json(res).await
|
}
|
||||||
|
|
||||||
|
/// 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)]
|
#[doc(hidden)]
|
||||||
|
@ -358,7 +401,7 @@ mod tests {
|
||||||
assert_eq!(result, Bytes::from_static(b"delete!"));
|
assert_eq!(result, Bytes::from_static(b"delete!"));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
pub struct Person {
|
pub struct Person {
|
||||||
id: String,
|
id: String,
|
||||||
name: String,
|
name: String,
|
||||||
|
@ -383,6 +426,26 @@ mod tests {
|
||||||
assert_eq!(&result.id, "12345");
|
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]
|
#[actix_rt::test]
|
||||||
async fn test_body_json() {
|
async fn test_body_json() {
|
||||||
let app = init_service(App::new().service(web::resource("/people").route(
|
let app = init_service(App::new().service(web::resource("/people").route(
|
||||||
|
@ -403,6 +466,27 @@ mod tests {
|
||||||
assert_eq!(&result.name, "User name");
|
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]
|
#[actix_rt::test]
|
||||||
async fn test_request_response_form() {
|
async fn test_request_response_form() {
|
||||||
let app = init_service(App::new().service(web::resource("/people").route(
|
let app = init_service(App::new().service(web::resource("/people").route(
|
||||||
|
|
Loading…
Reference in a new issue