1
0
Fork 0
mirror of https://github.com/actix/actix-web.git synced 2025-01-08 08:15:34 +00:00
actix-web/guide/src/qs_8.md

177 lines
5.7 KiB
Markdown
Raw Normal View History

2017-12-27 01:14:37 +00:00
# Testing
2018-03-28 20:16:01 +00:00
Every application should be well tested. Actix provides tools to perform unit and
2017-12-27 01:14:37 +00:00
integration tests.
2017-12-27 03:48:02 +00:00
## Unit tests
2018-04-06 22:03:30 +00:00
For unit testing, actix provides a request builder type and simple handler runner.
2018-03-28 20:16:01 +00:00
[*TestRequest*](../actix_web/test/struct.TestRequest.html) implements a builder-like pattern.
2018-04-06 22:03:30 +00:00
You can generate a `HttpRequest` instance with `finish()`, or you can
2018-03-28 20:16:01 +00:00
run your handler with `run()` or `run_async()`.
2017-12-27 03:48:02 +00:00
```rust
# extern crate actix_web;
use actix_web::{http, test, HttpRequest, HttpResponse, HttpMessage};
2017-12-27 03:48:02 +00:00
fn index(req: HttpRequest) -> HttpResponse {
if let Some(hdr) = req.headers().get(http::header::CONTENT_TYPE) {
2017-12-27 03:48:02 +00:00
if let Ok(s) = hdr.to_str() {
return HttpResponse::Ok().into()
2017-12-27 03:48:02 +00:00
}
}
HttpResponse::BadRequest().into()
2017-12-27 03:48:02 +00:00
}
fn main() {
let resp = test::TestRequest::with_header("content-type", "text/plain")
2017-12-27 03:48:02 +00:00
.run(index)
.unwrap();
assert_eq!(resp.status(), http::StatusCode::OK);
2017-12-27 03:48:02 +00:00
let resp = test::TestRequest::default()
2017-12-27 03:48:02 +00:00
.run(index)
.unwrap();
assert_eq!(resp.status(), http::StatusCode::BAD_REQUEST);
2017-12-27 03:48:02 +00:00
}
```
2017-12-27 01:14:37 +00:00
## Integration tests
2018-04-06 22:03:30 +00:00
There are several methods for testing your application. Actix provides
[*TestServer*](../actix_web/test/struct.TestServer.html), which can be used
to run the application with specific handlers in a real http server.
`TestServer::get()`, `TestServer::post()`, or `TestServer::client()`
2018-03-28 20:16:01 +00:00
methods can be used to send requests to the test server.
2017-12-27 01:14:37 +00:00
2018-04-06 22:03:30 +00:00
A simple form `TestServer` can be configured to use a handler.
`TestServer::new` method accepts a configuration function, and the only argument
for this function is a *test application* instance.
> Check the [api documentation](../actix_web/test/struct.TestApp.html) for more information.
2017-12-27 01:14:37 +00:00
```rust
# extern crate actix_web;
use actix_web::{HttpRequest, HttpResponse, HttpMessage};
2017-12-27 01:14:37 +00:00
use actix_web::test::TestServer;
fn index(req: HttpRequest) -> HttpResponse {
HttpResponse::Ok().into()
2017-12-27 01:14:37 +00:00
}
fn main() {
2018-02-19 21:18:18 +00:00
let mut srv = TestServer::new(|app| app.handler(index)); // <- Start new test server
2018-03-02 03:12:59 +00:00
2018-02-19 21:18:18 +00:00
let request = srv.get().finish().unwrap(); // <- create client request
let response = srv.execute(request.send()).unwrap(); // <- send request to the server
assert!(response.status().is_success()); // <- check response
2018-03-02 03:12:59 +00:00
2018-02-19 21:18:18 +00:00
let bytes = srv.execute(response.body()).unwrap(); // <- read response body
2017-12-27 01:14:37 +00:00
}
```
2018-04-06 22:03:30 +00:00
The other option is to use an application factory. In this case, you need to pass the factory
function the same way as you would for real http server configuration.
2017-12-27 01:14:37 +00:00
```rust
# extern crate actix_web;
2018-03-31 07:16:55 +00:00
use actix_web::{http, test, App, HttpRequest, HttpResponse};
2017-12-27 01:14:37 +00:00
fn index(req: HttpRequest) -> HttpResponse {
HttpResponse::Ok().into()
2017-12-27 01:14:37 +00:00
}
/// This function get called by http server.
2018-03-31 07:16:55 +00:00
fn create_app() -> App {
App::new()
2017-12-27 01:14:37 +00:00
.resource("/test", |r| r.h(index))
}
fn main() {
let mut srv = test::TestServer::with_factory(create_app); // <- Start new test server
2018-02-19 21:18:18 +00:00
let request = srv.client(
http::Method::GET, "/test").finish().unwrap(); // <- create client request
let response = srv.execute(request.send()).unwrap(); // <- send request to the server
2018-02-19 21:18:18 +00:00
assert!(response.status().is_success()); // <- check response
2017-12-27 01:14:37 +00:00
}
```
2018-01-30 23:13:33 +00:00
2018-04-06 22:03:30 +00:00
If you need more complex application configuration, use the `TestServer::build_with_state()`
method. For example, you may need to initialize application state or start `SyncActor`'s for diesel
interation. This method accepts a closure that constructs the application state,
and it runs when the actix system is configured. Thus, you can initialize any additional actors.
2018-03-30 02:22:43 +00:00
```rust,ignore
#[test]
fn test() {
let srv = TestServer::build_with_state(|| { // <- construct builder with config closure
// we can start diesel actors
let addr = SyncArbiter::start(3, || {
DbExecutor(SqliteConnection::establish("test.db").unwrap())
});
// then we can construct custom state, or it could be `()`
MyState{addr: addr}
})
.start(|app| { // <- register server handlers and start test server
app.resource(
"/{username}/index.html", |r| r.with(
|p: Path<PParam>| format!("Welcome {}!", p.username)));
});
// now we can run our test code
);
```
2018-01-30 23:13:33 +00:00
## WebSocket server tests
2018-04-06 22:03:30 +00:00
It is possible to register a *handler* with `TestApp::handler()`, which
initiates a web socket connection. *TestServer* provides the method `ws()`, which connects to
2018-03-28 20:16:01 +00:00
the websocket server and returns ws reader and writer objects. *TestServer* also
2018-04-06 22:03:30 +00:00
provides an `execute()` method, which runs future objects to completion and returns
2018-01-30 23:13:33 +00:00
result of the future computation.
2018-03-28 20:16:01 +00:00
Here is a simple example that shows how to test server websocket handler.
2018-01-30 23:13:33 +00:00
```rust
# extern crate actix;
# extern crate actix_web;
# extern crate futures;
# extern crate http;
# extern crate bytes;
use actix_web::*;
use futures::Stream;
# use actix::prelude::*;
struct Ws; // <- WebSocket actor
impl Actor for Ws {
type Context = ws::WebsocketContext<Self>;
}
2018-03-29 17:01:07 +00:00
impl StreamHandler<ws::Message, ws::ProtocolError> for Ws {
2018-01-30 23:13:33 +00:00
fn handle(&mut self, msg: ws::Message, ctx: &mut Self::Context) {
match msg {
2018-02-10 08:05:20 +00:00
ws::Message::Text(text) => ctx.text(text),
2018-01-30 23:13:33 +00:00
_ => (),
}
}
}
fn main() {
let mut srv = test::TestServer::new( // <- start our server with ws handler
|app| app.handler(|req| ws::start(req, Ws)));
let (reader, mut writer) = srv.ws().unwrap(); // <- connect to ws server
writer.text("text"); // <- send message to server
2018-03-28 20:16:01 +00:00
2018-01-30 23:13:33 +00:00
let (item, reader) = srv.execute(reader.into_future()).unwrap(); // <- wait for one message
assert_eq!(item, Some(ws::Message::Text("text".to_owned())));
}
```