mirror of
https://github.com/actix/actix-website
synced 2025-02-02 04:09:06 +01:00
update testing to actix-web 2.0.0 and improve stream testing code (#141)
* update testing to actix-web 2.0.0 and improve stream testing code
This commit is contained in:
parent
13f0756d33
commit
bc4e0422c8
@ -11,10 +11,9 @@ integration tests.
|
||||
|
||||
# Unit Tests
|
||||
|
||||
For unit testing, actix-web provides a request builder type and a simple handler runner.
|
||||
[*TestRequest*][testrequest] implements a builder-like pattern. You can generate a
|
||||
`HttpRequest` instance with `to_http_request()`, or you can
|
||||
run your handler with `block_on()`.
|
||||
For unit testing, actix-web provides a request builder type.
|
||||
[*TestRequest*][testrequest] implements a builder-like pattern. You can generate a
|
||||
`HttpRequest` instance with `to_http_request()` and call your handler with it.
|
||||
|
||||
{{< include-example example="testing" file="main.rs" section="unit-tests" >}}
|
||||
|
||||
@ -23,7 +22,7 @@ run your handler with `block_on()`.
|
||||
There a few methods for testing your application. Actix-web can be used
|
||||
to run the application with specific handlers in a real http server.
|
||||
|
||||
`TestRequest::get()`, `TestRequest::post()`, and `TestRequest::client()`
|
||||
`TestRequest::get()`, `TestRequest::post()` and other
|
||||
methods can be used to send requests to the test server.
|
||||
|
||||
To create a `Service` for testing, use the `test::init_service` method which accepts a
|
||||
@ -33,15 +32,6 @@ regular `App` builder.
|
||||
|
||||
{{< include-example example="testing" file="integration_one.rs" section="integration-one" >}}
|
||||
|
||||
Note: If you get the error message "no Task is currently running" when running your
|
||||
test, you may need to wrap your service call in
|
||||
[test::run_on](https://docs.rs/actix-web/1/actix_web/test/fn.run_on.html):
|
||||
|
||||
```rust
|
||||
let future = test::run_on(|| app.call(req));
|
||||
let resp = test::block_on(future).unwrap();
|
||||
```
|
||||
|
||||
If you need more complex application configuration testing should be very similar to creating
|
||||
the normal application. For example, you may need to initialize application state. Create an
|
||||
`App` with a `data` method and attach state just like you would from a normal application.
|
||||
@ -50,13 +40,13 @@ the normal application. For example, you may need to initialize application stat
|
||||
|
||||
# Stream response tests
|
||||
|
||||
If you need to test stream it would be enough to convert a [*ClientResponse*][clientresponse]
|
||||
If you need to test stream it would be enough to call `take_body()` and convert a resulting [*ResponseBody*][responsebody]
|
||||
to future and execute it.
|
||||
For example of testing [*Server Sent Events*][serversentevents].
|
||||
|
||||
{{< include-example example="testing" file="stream_response.rs" section="stream-response" >}}
|
||||
|
||||
[serversentevents]: https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events
|
||||
[clientresponse]: https://docs.rs/actix-web/1/actix_web/client/struct.ClientResponse.html
|
||||
[actixdocs]: https://docs.rs/actix-web/1/actix_web/test/index.html
|
||||
[testrequest]: https://docs.rs/actix-web/1/actix_web/error/trait.ResponseError.html#foreign-impls
|
||||
[responsebody]: https://docs.rs/actix-web/2/actix_web/body/enum.ResponseBody.html
|
||||
[actixdocs]: https://docs.rs/actix-web/2/actix_web/test/index.html
|
||||
[testrequest]: https://docs.rs/actix-web/2/actix_web/error/trait.ResponseError.html#foreign-impls
|
||||
|
@ -4,8 +4,10 @@ version = "1.0.0"
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
actix-web = "1.0"
|
||||
futures = "0.1"
|
||||
bytes = "0.4"
|
||||
actix-web = "2.0"
|
||||
actix-rt = "1.0"
|
||||
futures = "0.3"
|
||||
futures-util = "0.3"
|
||||
bytes = "0.5"
|
||||
serde = "1.0"
|
||||
serde_json = "1.0"
|
||||
|
@ -1,7 +1,7 @@
|
||||
use actix_web::{HttpRequest, Responder};
|
||||
|
||||
#[allow(dead_code)]
|
||||
fn index(_req: HttpRequest) -> impl Responder {
|
||||
async fn index(_req: HttpRequest) -> impl Responder {
|
||||
"Hello world!"
|
||||
}
|
||||
|
||||
@ -9,24 +9,21 @@ fn index(_req: HttpRequest) -> impl Responder {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use actix_web::dev::Service;
|
||||
use actix_web::{test, web, App};
|
||||
|
||||
#[test]
|
||||
fn test_index_get() {
|
||||
let mut app = test::init_service(App::new().route("/", web::get().to(index)));
|
||||
let req = test::TestRequest::get().uri("/").to_request();
|
||||
let resp = test::block_on(app.call(req)).unwrap();
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn test_index_get() {
|
||||
let mut app = test::init_service(App::new().route("/", web::get().to(index))).await;
|
||||
let req = test::TestRequest::with_header("content-type", "text/plain").to_request();
|
||||
let resp = test::call_service(&mut app, req).await;
|
||||
assert!(resp.status().is_success());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_index_post() {
|
||||
let mut app = test::init_service(App::new().route("/", web::get().to(index)));
|
||||
#[actix_rt::test]
|
||||
async fn test_index_post() {
|
||||
let mut app = test::init_service(App::new().route("/", web::get().to(index))).await;
|
||||
let req = test::TestRequest::post().uri("/").to_request();
|
||||
let resp = test::block_on(app.call(req)).unwrap();
|
||||
|
||||
let resp = test::call_service(&mut app, req).await;
|
||||
assert!(resp.status().is_client_error());
|
||||
}
|
||||
}
|
||||
|
@ -7,7 +7,7 @@ struct AppState {
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
fn index(data: web::Data<AppState>) -> impl Responder {
|
||||
async fn index(data: web::Data<AppState>) -> impl Responder {
|
||||
HttpResponse::Ok().json(data.get_ref())
|
||||
}
|
||||
|
||||
@ -17,17 +17,17 @@ mod tests {
|
||||
use super::*;
|
||||
use actix_web::{test, web, App};
|
||||
|
||||
#[test]
|
||||
fn test_index_get() {
|
||||
#[actix_rt::test]
|
||||
async fn test_index_get() {
|
||||
let mut app = test::init_service(
|
||||
App::new()
|
||||
.data(AppState { count: 4 })
|
||||
.route("/", web::get().to(index)),
|
||||
);
|
||||
).await;
|
||||
let req = test::TestRequest::get().uri("/").to_request();
|
||||
let resp: AppState = test::read_response_json(&mut app, req);
|
||||
let resp: AppState = test::read_response_json(&mut app, req).await;
|
||||
|
||||
assert!(resp.count == 4);
|
||||
assert_eq!(resp.count, 4);
|
||||
}
|
||||
}
|
||||
// </integration-two>
|
||||
|
@ -3,7 +3,7 @@ pub mod integration_two;
|
||||
pub mod stream_response;
|
||||
use actix_web::{http, web, App, HttpRequest, HttpResponse};
|
||||
|
||||
fn index(req: HttpRequest) -> HttpResponse {
|
||||
async fn index(req: HttpRequest) -> HttpResponse {
|
||||
if let Some(hdr) = req.headers().get(http::header::CONTENT_TYPE) {
|
||||
if let Ok(_s) = hdr.to_str() {
|
||||
return HttpResponse::Ok().into();
|
||||
@ -22,19 +22,17 @@ mod tests {
|
||||
use super::*;
|
||||
use actix_web::test;
|
||||
|
||||
#[test]
|
||||
fn test_index_ok() {
|
||||
let req = test::TestRequest::with_header("content-type", "text/plain")
|
||||
.to_http_request();
|
||||
|
||||
let resp = test::block_on(index(req)).unwrap();
|
||||
#[actix_rt::test]
|
||||
async fn test_index_ok() {
|
||||
let req = test::TestRequest::with_header("content-type", "text/plain").to_http_request();
|
||||
let resp = index(req).await;
|
||||
assert_eq!(resp.status(), http::StatusCode::OK);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_index_not_ok() {
|
||||
#[actix_rt::test]
|
||||
async fn test_index_not_ok() {
|
||||
let req = test::TestRequest::default().to_http_request();
|
||||
let resp = test::block_on(index(req)).unwrap();
|
||||
let resp = index(req).await;
|
||||
assert_eq!(resp.status(), http::StatusCode::BAD_REQUEST);
|
||||
}
|
||||
}
|
||||
|
@ -1,27 +1,30 @@
|
||||
// <stream-response>
|
||||
use std::task::Poll;
|
||||
use bytes::Bytes;
|
||||
use futures::stream::poll_fn;
|
||||
use futures::{Async, Poll};
|
||||
|
||||
use actix_web::http::{ContentEncoding, StatusCode};
|
||||
use actix_web::{middleware::BodyEncoding, web, App, Error, HttpRequest, HttpResponse};
|
||||
use actix_web::{web, http, App, Error, HttpRequest, HttpResponse};
|
||||
|
||||
fn sse(_req: HttpRequest) -> HttpResponse {
|
||||
async fn sse(_req: HttpRequest) -> HttpResponse {
|
||||
let mut counter: usize = 5;
|
||||
|
||||
// yields `data: N` where N in [5; 1]
|
||||
let server_events = poll_fn(move || -> Poll<Option<Bytes>, Error> {
|
||||
let server_events = poll_fn(move |_cx| -> Poll<Option<Result<Bytes, Error>>> {
|
||||
if counter == 0 {
|
||||
return Ok(Async::Ready(None));
|
||||
return Poll::Ready(None);
|
||||
}
|
||||
let payload = format!("data: {}\n\n", counter);
|
||||
counter -= 1;
|
||||
Ok(Async::Ready(Some(Bytes::from(payload))))
|
||||
Poll::Ready(Some(Ok(Bytes::from(payload))))
|
||||
});
|
||||
|
||||
HttpResponse::build(StatusCode::OK)
|
||||
.encoding(ContentEncoding::Identity)
|
||||
.content_type("text/event-stream")
|
||||
.set_header(http::header::CONTENT_TYPE, "text/event-stream")
|
||||
.set_header(
|
||||
http::header::CONTENT_ENCODING,
|
||||
ContentEncoding::Identity.as_str(),
|
||||
)
|
||||
.streaming(server_events)
|
||||
}
|
||||
|
||||
@ -32,18 +35,32 @@ pub fn main() {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use actix_rt;
|
||||
|
||||
use futures_util::stream::StreamExt;
|
||||
use futures_util::stream::TryStreamExt;
|
||||
|
||||
use actix_web::{test, web, App};
|
||||
|
||||
#[test]
|
||||
fn test_stream() {
|
||||
let mut app = test::init_service(App::new().route("/", web::get().to(sse)));
|
||||
#[actix_rt::test]
|
||||
async fn test_stream() {
|
||||
let mut app = test::init_service(App::new().route("/", web::get().to(sse))).await;
|
||||
let req = test::TestRequest::get().to_request();
|
||||
let resp = test::read_response(&mut app, req);
|
||||
assert!(
|
||||
resp == Bytes::from_static(
|
||||
b"data: 5\n\ndata: 4\n\ndata: 3\n\ndata: 2\n\ndata: 1\n\n"
|
||||
)
|
||||
);
|
||||
|
||||
let mut resp = test::call_service(&mut app, req).await;
|
||||
assert!(resp.status().is_success());
|
||||
|
||||
// first chunk
|
||||
let (bytes, mut resp) = resp.take_body().into_future().await;
|
||||
assert_eq!(bytes.unwrap().unwrap(), Bytes::from_static(b"data: 5\n\n"));
|
||||
|
||||
// second chunk
|
||||
let (bytes, mut resp) = resp.take_body().into_future().await;
|
||||
assert_eq!(bytes.unwrap().unwrap(), Bytes::from_static(b"data: 4\n\n"));
|
||||
|
||||
// remaining part
|
||||
let bytes = test::load_stream(resp.take_body().into_stream()).await;
|
||||
assert_eq!(bytes.unwrap(), Bytes::from_static(b"data: 3\n\ndata: 2\n\ndata: 1\n\n"));
|
||||
}
|
||||
}
|
||||
// </stream-response>
|
||||
|
Loading…
x
Reference in New Issue
Block a user