mirror of
https://github.com/rwf2/Rocket.git
synced 2025-01-29 12:52:05 +00:00
Update the testing guide for 0.3.
This commit is contained in:
parent
05a8a93eec
commit
b2ab3861b4
@ -21,8 +21,10 @@ aspect of Rocket. The sections are:
|
||||
parsing, and validating.
|
||||
- **[Responses](responses/):** discusses generating responses.
|
||||
- **[State](state/):** how to manage state in a Rocket application.
|
||||
- **[Fairings](fairings/):** introduction to Rocket's structure middleware.
|
||||
- **[Testing](testing/):** how to unit and integration test a Rocket
|
||||
application.
|
||||
- **[Configuration](configuration/):** how to configure a Rocket application.
|
||||
- **[Pastebin](pastebin/):** a tutorial on how to create a pastebin with
|
||||
Rocket.
|
||||
- **[Conclusion](conclusion/):** concludes the guide and discusses next steps
|
||||
|
@ -1,59 +1,118 @@
|
||||
# Testing
|
||||
|
||||
Every application should be well tested. Rocket provides the tools to perform
|
||||
unit and integration tests on your application as well as inspect Rocket
|
||||
generated code.
|
||||
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.
|
||||
|
||||
## Tests
|
||||
## Local Dispatching
|
||||
|
||||
Rocket includes a built-in [testing](https://api.rocket.rs/rocket/testing/)
|
||||
module that allows you to unit and integration test your Rocket applications.
|
||||
Testing is simple:
|
||||
Rocket applications are tested by dispatching requests to a local instance of
|
||||
`Rocket`. The [`local`] module contains all of the structures necessary to do
|
||||
so. In particular, it contains a [`Client`] structure that is used to create
|
||||
[`LocalRequest`] structures that can be dispatched against a given [`Rocket`]
|
||||
instance. Usage is straightforward:
|
||||
|
||||
1. Construct a `Rocket` instance.
|
||||
2. Construct a `MockRequest`.
|
||||
3. Dispatch the request using the `Rocket` instance.
|
||||
4. Inspect, validate, and verify the `Response`.
|
||||
1. Construct a `Rocket` instance that represents the application.
|
||||
|
||||
After setting up, we'll walk through each of these steps for the "Hello, world!"
|
||||
program below:
|
||||
```rust
|
||||
let rocket = rocket::ignite();
|
||||
```
|
||||
|
||||
2. Construct a `Client` using the `Rocket` instance.
|
||||
|
||||
```rust
|
||||
let client = Client::new(rocket).expect("valid rocket instance");
|
||||
```
|
||||
|
||||
3. Construct requests using the `Client` instance.
|
||||
|
||||
```rust
|
||||
let req = client.get("/");
|
||||
```
|
||||
|
||||
3. Dispatch the request to retrieve the response.
|
||||
|
||||
```rust
|
||||
let response = req.dispatch();
|
||||
```
|
||||
|
||||
[`local`]: https://api.rocket.rs/rocket/local/index.html
|
||||
[`Client`]: https://api.rocket.rs/rocket/local/struct.Client.html
|
||||
[`LocalRequest`]: https://api.rocket.rs/rocket/local/struct.LocalRequest.html
|
||||
[`Rocket`]: https://api.rocket.rs/rocket/struct.Rocket.html
|
||||
|
||||
## Validating Responses
|
||||
|
||||
A `dispatch` of a `LocalRequest` returns a [`LocalResponse`] which can be used
|
||||
transparently as a [`Response`] value. 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.
|
||||
|
||||
The [`Response`] type provides methods to ease this sort of validation. We list
|
||||
a few below:
|
||||
|
||||
* [`status`]: returns the HTTP status in the response.
|
||||
* [`content_type`]: returns the Content-Type header in the response.
|
||||
* [`headers`]: returns a map of all of the headers in the response.
|
||||
* [`body_string`]: returns the body data as a `String`.
|
||||
* [`body_bytes`]: returns the body data as a `Vec<u8>`.
|
||||
|
||||
[`LocalResponse`]: https://api.rocket.rs/rocket/local/struct.LocalResponse.html
|
||||
[`Response`]: https://api.rocket.rs/rocket/struct.Response.html
|
||||
[`status`]: https://api.rocket.rs/rocket/struct.Response.html#method.status
|
||||
[`content_type`]: https://api.rocket.rs/rocket/struct.Response.html#method.content_type
|
||||
[`headers`]: https://api.rocket.rs/rocket/struct.Response.html#method.headers
|
||||
[`body_string`]: https://api.rocket.rs/rocket/struct.Response.html#method.body_string
|
||||
[`body_bytes`]: https://api.rocket.rs/rocket/struct.Response.html#method.body_bytes
|
||||
|
||||
These methods are typically used in combination with the `assert_eq!` or
|
||||
`assert!` macros as follows:
|
||||
|
||||
```rust
|
||||
#![feature(plugin)]
|
||||
#![plugin(rocket_codegen)]
|
||||
let rocket = rocket::ignite();
|
||||
let client = Client::new(rocket).expect("valid rocket instance");
|
||||
let mut response = client.get("/").dispatch();
|
||||
|
||||
extern crate rocket;
|
||||
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.body_string(), Some("Expected Body.".into()));
|
||||
```
|
||||
|
||||
## 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!"
|
||||
}
|
||||
|
||||
fn rocket() -> Rocket {
|
||||
rocket::ignite().mount("/", routes![hello])
|
||||
}
|
||||
|
||||
fn main() {
|
||||
rocket().launch();
|
||||
}
|
||||
```
|
||||
|
||||
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
|
||||
|
||||
For the `testing` module to be available, Rocket needs to be compiled with the
|
||||
_testing_ feature enabled. Since this feature should only be enabled when your
|
||||
application is compiled for testing, the recommended way to enable the _testing_
|
||||
feature is via Cargo's `[dev-dependencies]` section in the `Cargo.toml` file as
|
||||
follows:
|
||||
|
||||
```toml
|
||||
[dev-dependencies]
|
||||
rocket = { version = "0.2.8", features = ["testing"] }
|
||||
```
|
||||
|
||||
With this in place, running `cargo test` will result in Cargo compiling Rocket
|
||||
with the _testing_ feature, thus enabling the `testing` module.
|
||||
|
||||
You'll also need a `test` module with the proper imports:
|
||||
First, we'll create a `test` module with the proper imports:
|
||||
|
||||
```rust
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::rocket;
|
||||
use rocket::testing::MockRequest;
|
||||
use rocket::http::{Status, Method};
|
||||
use rocket::local::Client;
|
||||
use rocket::http::Status;
|
||||
|
||||
#[test]
|
||||
fn hello_world() {
|
||||
@ -62,75 +121,89 @@ mod test {
|
||||
}
|
||||
```
|
||||
|
||||
In the remainder of this section, we'll work on filling in the `hello_world`
|
||||
testing function to ensure that the `hello` route results in a `Response` with
|
||||
_"Hello, world!"_ in the body.
|
||||
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
|
||||
|
||||
We'll begin by constructing a `Rocket` instance with the `hello` route mounted
|
||||
at the root path. We do this in the same way we would normally with one
|
||||
exception: we need to refer to the `testing` route in the `super` namespace:
|
||||
To test our "Hello, world!" application, we first 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 rocket = rocket::ignite().mount("/", routes![super::hello]);
|
||||
let client = Client::new(rocket()).expect("valid rocket instance");
|
||||
```
|
||||
|
||||
Next, we create a `MockRequest` that issues a `Get` request to the `"/"` path:
|
||||
Then, we create a new `GET /` request and dispatch it, getting back our
|
||||
application's response:
|
||||
|
||||
```rust
|
||||
let mut req = MockRequest::new(Method::Get, "/");
|
||||
let mut response = client.get("/").dispatch();
|
||||
```
|
||||
|
||||
We now ask Rocket to perform a full dispatch, which includes routing,
|
||||
pre-processing and post-processing, and retrieve the `Response`:
|
||||
|
||||
```rust
|
||||
let mut response = req.dispatch_with(&rocket);
|
||||
```
|
||||
|
||||
Finally, we can test the
|
||||
[Response](https://api.rocket.rs/rocket/struct.Response.html) values to ensure
|
||||
that it contains the information we expect it to. We want to ensure two things:
|
||||
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 querying the `Response` object directly:
|
||||
We do this by checking the `Response` object directly:
|
||||
|
||||
```rust
|
||||
assert_eq!(response.status(), Status::Ok);
|
||||
|
||||
let body_str = response.body().and_then(|b| b.into_string());
|
||||
assert_eq!(body_str, Some("Hello, world!".to_string()));
|
||||
assert_eq!(response.body_string(), Some("Hello, world!".into()));
|
||||
```
|
||||
|
||||
That's it! Run the tests with `cargo test`. The complete application, with
|
||||
testing, can be found in the [GitHub testing
|
||||
example](https://github.com/SergioBenitez/Rocket/tree/v0.2.8/examples/testing).
|
||||
That's it! Altogether, this looks like:
|
||||
|
||||
```rust
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::rocket;
|
||||
use rocket::local::Client;
|
||||
use rocket::http::Status;
|
||||
|
||||
#[test]
|
||||
fn hello_world() {
|
||||
let client = Client::new(rocket()).expect("valid rocket instance");
|
||||
let mut response = client.get("/").dispatch();
|
||||
assert_eq!(response.status(), Status::Ok);
|
||||
assert_eq!(response.body_string(), Some("Hello, world!".into()));
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The tests can be run with `cargo test`. You can find the full source code to
|
||||
[this example on
|
||||
GitHub](https://github.com/SergioBenitez/Rocket/tree/v0.2.8/examples/testing).
|
||||
|
||||
## Codegen Debug
|
||||
|
||||
It is sometimes useful to inspect the code that Rocket's code generation is
|
||||
emitting, especially when you get a strange type error. To have Rocket log the
|
||||
code that it is emitting to the console, set the `ROCKET_CODEGEN_DEBUG`
|
||||
environment variable when compiling:
|
||||
It can be useful to inspect the code that Rocket's code generation is emitting,
|
||||
especially when you get a strange type error. To have Rocket log the code that
|
||||
it is emitting to the console, set the `ROCKET_CODEGEN_DEBUG` environment
|
||||
variable when compiling:
|
||||
|
||||
```rust
|
||||
ROCKET_CODEGEN_DEBUG=1 cargo build
|
||||
```
|
||||
|
||||
During compilation, you should see output like this:
|
||||
During compilation, you should see output like:
|
||||
|
||||
```rust
|
||||
Emitting item:
|
||||
fn rocket_route_fn_hello<'_b>(_req: &'_b ::rocket::Request,
|
||||
_data: ::rocket::Data)
|
||||
-> ::rocket::handler::Outcome<'_b> {
|
||||
fn rocket_route_fn_hello<'_b>(
|
||||
__req: &'_b ::rocket::Request,
|
||||
__data: ::rocket::Data
|
||||
) -> ::rocket::handler::Outcome<'_b> {
|
||||
let responder = hello();
|
||||
::rocket::handler::Outcome::from(_req, responder)
|
||||
::rocket::handler::Outcome::from(__req, responder)
|
||||
}
|
||||
```
|
||||
|
||||
This corresponds to the facade request handler Rocket generated for the `hello`
|
||||
route.
|
||||
This corresponds to the facade request handler Rocket has generated for the
|
||||
`hello` route.
|
||||
|
Loading…
Reference in New Issue
Block a user