update rocket
This commit is contained in:
parent
56e833dee2
commit
0692fb56ba
1 changed files with 149 additions and 0 deletions
|
@ -1344,6 +1344,155 @@ rocket::build()
|
|||
})));
|
||||
```
|
||||
|
||||
## Testing
|
||||
Every application should be well tested and understandable. Rocket provides the tools to perform unit and integration tests. It also provides a means to inspect code generated by Rocket.
|
||||
|
||||
### Local Dispatching
|
||||
Rocket applications are tested by dispatching requests to a local instance of `Rocket`. The [`local`](https://api.rocket.rs/v0.5/rocket/local/) module contains all of the structures necessary to do so. In particular, it contains a [`Client`](https://api.rocket.rs/v0.5/rocket/local/#client) structure that is used to create [`LocalRequest`](https://api.rocket.rs/v0.5/rocket/local/#localrequest) structures that can be dispatched against a given [`Rocket`](https://api.rocket.rs/v0.5/rocket/struct.Rocket.html) instance. Usage is straightforward:
|
||||
|
||||
1. Construct a `Rocket` instance that represents the application.
|
||||
```rust
|
||||
let rocket = rocket::build();
|
||||
```
|
||||
2. Construct a `Client` using the `Rocket` instance.
|
||||
```rust
|
||||
let client = Client::tracked(rocket).unwrap();
|
||||
```
|
||||
3. Construct requests using the `Client` instance.
|
||||
```rust
|
||||
let req = client.get("/");
|
||||
```
|
||||
4. Dispatch the request to retrieve the response.
|
||||
```rust
|
||||
let response = req.dispatch();
|
||||
```
|
||||
|
||||
### Validating Responses
|
||||
A `dispatch` of a `LocalRequest` returns a [`LocalResponse`](https://api.rocket.rs/v0.5/rocket/local/blocking/struct.LocalResponse.html) which can be inspected for validity. During testing, the response is usually validated against expected properties. These includes things like the response HTTP status, the inclusion of headers, and expected body data.
|
||||
|
||||
[`LocalResponse`](https://api.rocket.rs/v0.5/rocket/local/blocking/struct.LocalResponse.html) type provides methods to ease this sort of validation. We list a few below:
|
||||
|
||||
- [`status`](https://api.rocket.rs/v0.5/rocket/local/blocking/struct.LocalResponse.html#method.status): returns the HTTP status in the response.
|
||||
- [`content_type`](https://api.rocket.rs/v0.5/rocket/local/blocking/struct.LocalResponse.html#method.content_type): returns the Content-Type header in the response.
|
||||
- [`headers`](https://api.rocket.rs/v0.5/rocket/local/blocking/struct.LocalResponse.html#method.headers): returns a map of all of the headers in the response.
|
||||
- [`into_string`](https://api.rocket.rs/v0.5/rocket/local/blocking/struct.LocalResponse.html#method.into_string): reads the body data into a `String`.
|
||||
- [`into_bytes`](https://api.rocket.rs/v0.5/rocket/local/blocking/struct.LocalResponse.html#method.into_bytes): reads the body data into a `Vec<u8>`.
|
||||
- [`into_json`](https://api.rocket.rs/v0.5/rocket/local/blocking/struct.LocalResponse.html#method.into_json): deserializes the body data on-the-fly as JSON.
|
||||
- [`into_msgpack`](https://api.rocket.rs/v0.5/rocket/local/blocking/struct.LocalResponse.html#method.into_msgpack): deserializes the body data on-the-fly as MessagePack.
|
||||
|
||||
These methods are typically used in combination with the `assert_eq!` or `assert!` macros as follows:
|
||||
|
||||
```rust
|
||||
use rocket::http::{ContentType, Status};
|
||||
|
||||
let mut response = client.get(uri!(hello)).dispatch();
|
||||
|
||||
assert_eq!(response.status(), Status::Ok);
|
||||
assert_eq!(response.content_type(), Some(ContentType::Plain));
|
||||
assert!(response.headers().get_one("X-Special").is_some());
|
||||
assert_eq!(response.into_string().unwrap(), "Expected Body");
|
||||
```
|
||||
|
||||
### Testing "Hello, world!"
|
||||
To solidify an intuition for how Rocket applications are tested, we walk through how to test the "Hello, world!" application below:
|
||||
|
||||
```rust
|
||||
#[get("/")]
|
||||
fn hello() -> &'static str {
|
||||
"Hello, world!"
|
||||
}
|
||||
|
||||
#[launch]
|
||||
fn rocket() -> _ {
|
||||
rocket::build().mount("/", routes![hello])
|
||||
}
|
||||
```
|
||||
|
||||
Notice that we've separated the _creation_ of the `Rocket` instance from the _launch_ of the instance. As you'll soon see, this makes testing our application easier, less verbose, and less error-prone.
|
||||
|
||||
#### Setting Up
|
||||
First, we'll create a `test` module with the proper imports:
|
||||
|
||||
```rust
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::rocket;
|
||||
use rocket::local::blocking::Client;
|
||||
use rocket::http::Status;
|
||||
|
||||
#[test]
|
||||
fn hello_world() {
|
||||
/* .. */
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
You can also move the body of the `test` module into its own file, say `tests.rs`, and then import the module into the main file using:
|
||||
|
||||
```rust
|
||||
#[cfg(test)] mod tests;
|
||||
```
|
||||
|
||||
#### Testing
|
||||
To test our "Hello, world!" application, we create a `Client` for our `Rocket` instance. It's okay to use methods like `expect` and `unwrap` during testing: we _want_ our tests to panic when something goes wrong.
|
||||
|
||||
```rust
|
||||
let client = Client::tracked(rocket()).expect("valid rocket instance");
|
||||
```
|
||||
|
||||
Then, we create a new `GET /` request and dispatch it, getting back our application's response:
|
||||
|
||||
```rust
|
||||
let mut response = client.get(uri!(hello)).dispatch();
|
||||
```
|
||||
|
||||
Finally, we ensure that the response contains the information we expect it to. Here, we want to ensure two things:
|
||||
|
||||
1. The status is `200 OK`.
|
||||
2. The body is the string "Hello, world!".
|
||||
|
||||
We do this by checking the `Response` object directly:
|
||||
|
||||
```rust
|
||||
use rocket::http::{ContentType, Status};
|
||||
|
||||
assert_eq!(response.status(), Status::Ok);
|
||||
assert_eq!(response.into_string(), Some("Hello, world!".into()));
|
||||
```
|
||||
|
||||
That's it! Altogether, this looks like:
|
||||
```rust
|
||||
#[get("/")]
|
||||
fn hello() -> &'static str {
|
||||
"Hello, world!"
|
||||
}
|
||||
|
||||
#[launch]
|
||||
fn rocket() -> Rocket<Build> {
|
||||
rocket::build().mount("/", routes![hello])
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::rocket;
|
||||
use rocket::local::blocking::Client;
|
||||
use rocket::http::Status;
|
||||
|
||||
#[test]
|
||||
fn hello_world() {
|
||||
let client = Client::tracked(rocket()).expect("valid rocket instance");
|
||||
let mut response = client.get(uri!(super::hello)).dispatch();
|
||||
assert_eq!(response.status(), Status::Ok);
|
||||
assert_eq!(response.into_string().unwrap(), "Hello, world!");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The tests can be run with `cargo test`. You can find the full source code to [this example on GitHub](https://github.com/rwf2/Rocket/tree/v0.5/examples/testing).
|
||||
|
||||
### Asynchronous Testing
|
||||
You may have noticed the use of a "`blocking`" API in these examples, even though `Rocket` is an `async` web framework. In most situations, the `blocking` testing API is easier to use and should be preferred. However, when concurrent execution of two or more requests is required for the server to make progress, you will need the more flexible `asynchronous` API; the `blocking` API is not capable of dispatching multiple requests simultaneously. While synthetic, the [`async_required` `testing` example](https://github.com/rwf2/Rocket/tree/v0.5/examples/testing/src/async_required.rs) uses an `async` barrier to demonstrate such a case. For more information, see the [`rocket::local`](https://api.rocket.rs/v0.5/rocket/local/index.html) and [`rocket::local::asynchronous`](https://api.rocket.rs/v0.5/rocket/local/asynchronous/index.html) documentation.
|
||||
|
||||
## Configuration
|
||||
Rocket's configuration system is flexible. Based on [Figment](https://docs.rs/figment/0.10/figment), it allows you to configure your application the way _you_ want while also providing with a sensible set of defaults.
|
||||
|
||||
|
|
Loading…
Reference in a new issue