2022-06-10 18:21:15 +00:00
|
|
|
+++
|
|
|
|
summary = "unit and integration testing with the built-in testing library"
|
|
|
|
+++
|
|
|
|
|
2017-04-17 02:48:59 +00:00
|
|
|
# Testing
|
|
|
|
|
2017-07-05 02:21:46 +00:00
|
|
|
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.
|
2017-04-17 02:48:59 +00:00
|
|
|
|
2017-07-05 02:21:46 +00:00
|
|
|
## Local Dispatching
|
2017-04-17 02:48:59 +00:00
|
|
|
|
2017-07-05 02:21:46 +00:00
|
|
|
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:
|
2017-04-17 02:48:59 +00:00
|
|
|
|
2017-07-05 02:21:46 +00:00
|
|
|
1. Construct a `Rocket` instance that represents the application.
|
2017-04-17 02:48:59 +00:00
|
|
|
|
Test 'secret_key' validation, now on pre-launch.
Prior to this commit, it was not possible to test Rocket crates in
production mode without setting a global secret key or bypassing secret
key checking - the testing script did the latter. The consequence is
that it became impossible to test secret key related failures because
the tests passed regardless.
This commit undoes this. As a consequence, all tests are now aware of
the difference between debug and release configurations, the latter of
which validates 'secret_key' by default. New 'Client::debug()' and
'Client::debug_with()' simplify creating an instance of 'Client' with
configuration in debug mode to avoid undesired test failures.
The summary of changes in this commit are:
* Config 'secret_key' success and failure are now tested.
* 'secret_key' validation was moved to pre-launch from 'Config:from()'.
* 'Config::from()' only extracts the config.
* Added 'Config::try_from()' for non-panicking extraction.
* 'Config' now knows the profile it was extracted from.
* The 'Config' provider sets a profile of 'Config.profile'.
* 'Rocket', 'Client', 'Fairings', implement 'Debug'.
* 'fairing::Info' implements 'Copy', 'Clone'.
* 'Fairings' keeps track of, logs attach fairings.
* 'Rocket::reconfigure()' was added to allow modifying a config.
Internally, the testing script was refactored to properly test the
codebase with the new changes. In particular, it no longer sets a rustc
'cfg' to avoid secret-key checking.
Resolves #1543.
Fixes #1564.
2021-03-09 08:07:43 +00:00
|
|
|
```rust,no_run
|
2021-04-08 08:07:52 +00:00
|
|
|
let rocket = rocket::build();
|
2020-02-15 11:43:47 +00:00
|
|
|
# let _ = rocket;
|
|
|
|
```
|
2017-07-05 02:21:46 +00:00
|
|
|
|
|
|
|
2. Construct a `Client` using the `Rocket` instance.
|
|
|
|
|
Test 'secret_key' validation, now on pre-launch.
Prior to this commit, it was not possible to test Rocket crates in
production mode without setting a global secret key or bypassing secret
key checking - the testing script did the latter. The consequence is
that it became impossible to test secret key related failures because
the tests passed regardless.
This commit undoes this. As a consequence, all tests are now aware of
the difference between debug and release configurations, the latter of
which validates 'secret_key' by default. New 'Client::debug()' and
'Client::debug_with()' simplify creating an instance of 'Client' with
configuration in debug mode to avoid undesired test failures.
The summary of changes in this commit are:
* Config 'secret_key' success and failure are now tested.
* 'secret_key' validation was moved to pre-launch from 'Config:from()'.
* 'Config::from()' only extracts the config.
* Added 'Config::try_from()' for non-panicking extraction.
* 'Config' now knows the profile it was extracted from.
* The 'Config' provider sets a profile of 'Config.profile'.
* 'Rocket', 'Client', 'Fairings', implement 'Debug'.
* 'fairing::Info' implements 'Copy', 'Clone'.
* 'Fairings' keeps track of, logs attach fairings.
* 'Rocket::reconfigure()' was added to allow modifying a config.
Internally, the testing script was refactored to properly test the
codebase with the new changes. In particular, it no longer sets a rustc
'cfg' to avoid secret-key checking.
Resolves #1543.
Fixes #1564.
2021-03-09 08:07:43 +00:00
|
|
|
```rust,no_run
|
2020-07-11 16:41:53 +00:00
|
|
|
# use rocket::local::blocking::Client;
|
2021-04-08 08:07:52 +00:00
|
|
|
# let rocket = rocket::build();
|
2020-10-15 04:37:16 +00:00
|
|
|
let client = Client::tracked(rocket).unwrap();
|
2020-02-15 11:43:47 +00:00
|
|
|
# let _ = client;
|
|
|
|
```
|
2017-07-05 02:21:46 +00:00
|
|
|
|
|
|
|
3. Construct requests using the `Client` instance.
|
|
|
|
|
Test 'secret_key' validation, now on pre-launch.
Prior to this commit, it was not possible to test Rocket crates in
production mode without setting a global secret key or bypassing secret
key checking - the testing script did the latter. The consequence is
that it became impossible to test secret key related failures because
the tests passed regardless.
This commit undoes this. As a consequence, all tests are now aware of
the difference between debug and release configurations, the latter of
which validates 'secret_key' by default. New 'Client::debug()' and
'Client::debug_with()' simplify creating an instance of 'Client' with
configuration in debug mode to avoid undesired test failures.
The summary of changes in this commit are:
* Config 'secret_key' success and failure are now tested.
* 'secret_key' validation was moved to pre-launch from 'Config:from()'.
* 'Config::from()' only extracts the config.
* Added 'Config::try_from()' for non-panicking extraction.
* 'Config' now knows the profile it was extracted from.
* The 'Config' provider sets a profile of 'Config.profile'.
* 'Rocket', 'Client', 'Fairings', implement 'Debug'.
* 'fairing::Info' implements 'Copy', 'Clone'.
* 'Fairings' keeps track of, logs attach fairings.
* 'Rocket::reconfigure()' was added to allow modifying a config.
Internally, the testing script was refactored to properly test the
codebase with the new changes. In particular, it no longer sets a rustc
'cfg' to avoid secret-key checking.
Resolves #1543.
Fixes #1564.
2021-03-09 08:07:43 +00:00
|
|
|
```rust,no_run
|
2020-07-11 16:41:53 +00:00
|
|
|
# use rocket::local::blocking::Client;
|
2021-04-08 08:07:52 +00:00
|
|
|
# let rocket = rocket::build();
|
2020-10-15 04:37:16 +00:00
|
|
|
# let client = Client::tracked(rocket).unwrap();
|
2020-02-15 11:43:47 +00:00
|
|
|
let req = client.get("/");
|
|
|
|
# let _ = req;
|
|
|
|
```
|
2017-07-05 02:21:46 +00:00
|
|
|
|
2017-07-10 11:59:55 +00:00
|
|
|
4. Dispatch the request to retrieve the response.
|
2017-07-05 02:21:46 +00:00
|
|
|
|
Test 'secret_key' validation, now on pre-launch.
Prior to this commit, it was not possible to test Rocket crates in
production mode without setting a global secret key or bypassing secret
key checking - the testing script did the latter. The consequence is
that it became impossible to test secret key related failures because
the tests passed regardless.
This commit undoes this. As a consequence, all tests are now aware of
the difference between debug and release configurations, the latter of
which validates 'secret_key' by default. New 'Client::debug()' and
'Client::debug_with()' simplify creating an instance of 'Client' with
configuration in debug mode to avoid undesired test failures.
The summary of changes in this commit are:
* Config 'secret_key' success and failure are now tested.
* 'secret_key' validation was moved to pre-launch from 'Config:from()'.
* 'Config::from()' only extracts the config.
* Added 'Config::try_from()' for non-panicking extraction.
* 'Config' now knows the profile it was extracted from.
* The 'Config' provider sets a profile of 'Config.profile'.
* 'Rocket', 'Client', 'Fairings', implement 'Debug'.
* 'fairing::Info' implements 'Copy', 'Clone'.
* 'Fairings' keeps track of, logs attach fairings.
* 'Rocket::reconfigure()' was added to allow modifying a config.
Internally, the testing script was refactored to properly test the
codebase with the new changes. In particular, it no longer sets a rustc
'cfg' to avoid secret-key checking.
Resolves #1543.
Fixes #1564.
2021-03-09 08:07:43 +00:00
|
|
|
```rust,no_run
|
2020-07-11 16:41:53 +00:00
|
|
|
# use rocket::local::blocking::Client;
|
2021-04-08 08:07:52 +00:00
|
|
|
# let rocket = rocket::build();
|
2020-10-15 04:37:16 +00:00
|
|
|
# let client = Client::tracked(rocket).unwrap();
|
2020-02-15 11:43:47 +00:00
|
|
|
# let req = client.get("/");
|
|
|
|
let response = req.dispatch();
|
|
|
|
# let _ = response;
|
|
|
|
```
|
2017-07-05 02:21:46 +00:00
|
|
|
|
2022-06-10 18:21:15 +00:00
|
|
|
[`local`]: @api/v0.5/rocket/local/
|
|
|
|
[`Client`]: @api/v0.5/rocket/local/#client
|
|
|
|
[`LocalRequest`]: @api/v0.5/rocket/local/#localrequest
|
|
|
|
[`Rocket`]: @api/v0.5/rocket/struct.Rocket.html
|
2017-07-05 02:21:46 +00:00
|
|
|
|
|
|
|
## Validating Responses
|
|
|
|
|
2021-03-20 01:09:13 +00:00
|
|
|
A `dispatch` of a `LocalRequest` returns a [`LocalResponse`] which can be
|
2021-04-20 20:18:10 +00:00
|
|
|
inspected for validity. During testing, the response is usually validated
|
2021-03-20 01:09:13 +00:00
|
|
|
against expected properties. These includes things like the response HTTP
|
|
|
|
status, the inclusion of headers, and expected body data.
|
2017-07-05 02:21:46 +00:00
|
|
|
|
2021-03-20 01:09:13 +00:00
|
|
|
[`LocalResponse`] type provides methods to ease this sort of validation. We list
|
2017-07-05 02:21:46 +00:00
|
|
|
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.
|
2021-03-20 01:09:13 +00:00
|
|
|
* [`into_string`]: reads the body data into a `String`.
|
|
|
|
* [`into_bytes`]: reads the body data into a `Vec<u8>`.
|
2021-04-29 12:19:24 +00:00
|
|
|
* [`into_json`]: deserializes the body data on-the-fly as JSON.
|
|
|
|
* [`into_msgpack`]: deserializes the body data on-the-fly as MessagePack.
|
2017-07-05 02:21:46 +00:00
|
|
|
|
2022-06-10 18:21:15 +00:00
|
|
|
[`LocalResponse`]: @api/v0.5/rocket/local/blocking/struct.LocalResponse.html
|
|
|
|
[`status`]: @api/v0.5/rocket/local/blocking/struct.LocalResponse.html#method.status
|
|
|
|
[`content_type`]: @api/v0.5/rocket/local/blocking/struct.LocalResponse.html#method.content_type
|
|
|
|
[`headers`]: @api/v0.5/rocket/local/blocking/struct.LocalResponse.html#method.headers
|
|
|
|
[`into_string`]: @api/v0.5/rocket/local/blocking/struct.LocalResponse.html#method.into_string
|
|
|
|
[`into_bytes`]: @api/v0.5/rocket/local/blocking/struct.LocalResponse.html#method.into_bytes
|
|
|
|
[`into_json`]: @api/v0.5/rocket/local/blocking/struct.LocalResponse.html#method.into_json
|
|
|
|
[`into_msgpack`]: @api/v0.5/rocket/local/blocking/struct.LocalResponse.html#method.into_msgpack
|
2017-07-05 02:21:46 +00:00
|
|
|
|
|
|
|
These methods are typically used in combination with the `assert_eq!` or
|
|
|
|
`assert!` macros as follows:
|
2017-04-17 02:48:59 +00:00
|
|
|
|
|
|
|
```rust
|
2020-02-15 11:43:47 +00:00
|
|
|
# #[macro_use] extern crate rocket;
|
|
|
|
|
|
|
|
# use std::io::Cursor;
|
|
|
|
# use rocket::Response;
|
|
|
|
# use rocket::http::Header;
|
2021-04-28 08:51:51 +00:00
|
|
|
#
|
|
|
|
# #[derive(Responder)]
|
|
|
|
# #[response(content_type = "text")]
|
|
|
|
# struct Custom {
|
|
|
|
# body: &'static str,
|
|
|
|
# header: Header<'static>,
|
|
|
|
# }
|
|
|
|
#
|
2020-02-15 11:43:47 +00:00
|
|
|
# #[get("/")]
|
2021-04-28 08:51:51 +00:00
|
|
|
# fn hello() -> Custom {
|
|
|
|
# Custom {
|
|
|
|
# body: "Expected Body",
|
|
|
|
# header: Header::new("X-Special", ""),
|
|
|
|
# }
|
2020-02-15 11:43:47 +00:00
|
|
|
# }
|
|
|
|
|
Test 'secret_key' validation, now on pre-launch.
Prior to this commit, it was not possible to test Rocket crates in
production mode without setting a global secret key or bypassing secret
key checking - the testing script did the latter. The consequence is
that it became impossible to test secret key related failures because
the tests passed regardless.
This commit undoes this. As a consequence, all tests are now aware of
the difference between debug and release configurations, the latter of
which validates 'secret_key' by default. New 'Client::debug()' and
'Client::debug_with()' simplify creating an instance of 'Client' with
configuration in debug mode to avoid undesired test failures.
The summary of changes in this commit are:
* Config 'secret_key' success and failure are now tested.
* 'secret_key' validation was moved to pre-launch from 'Config:from()'.
* 'Config::from()' only extracts the config.
* Added 'Config::try_from()' for non-panicking extraction.
* 'Config' now knows the profile it was extracted from.
* The 'Config' provider sets a profile of 'Config.profile'.
* 'Rocket', 'Client', 'Fairings', implement 'Debug'.
* 'fairing::Info' implements 'Copy', 'Clone'.
* 'Fairings' keeps track of, logs attach fairings.
* 'Rocket::reconfigure()' was added to allow modifying a config.
Internally, the testing script was refactored to properly test the
codebase with the new changes. In particular, it no longer sets a rustc
'cfg' to avoid secret-key checking.
Resolves #1543.
Fixes #1564.
2021-03-09 08:07:43 +00:00
|
|
|
# use rocket::local::blocking::Client;
|
2020-02-15 11:43:47 +00:00
|
|
|
use rocket::http::{ContentType, Status};
|
|
|
|
|
2021-04-08 08:07:52 +00:00
|
|
|
# let rocket = rocket::build().mount("/", routes![hello]);
|
Test 'secret_key' validation, now on pre-launch.
Prior to this commit, it was not possible to test Rocket crates in
production mode without setting a global secret key or bypassing secret
key checking - the testing script did the latter. The consequence is
that it became impossible to test secret key related failures because
the tests passed regardless.
This commit undoes this. As a consequence, all tests are now aware of
the difference between debug and release configurations, the latter of
which validates 'secret_key' by default. New 'Client::debug()' and
'Client::debug_with()' simplify creating an instance of 'Client' with
configuration in debug mode to avoid undesired test failures.
The summary of changes in this commit are:
* Config 'secret_key' success and failure are now tested.
* 'secret_key' validation was moved to pre-launch from 'Config:from()'.
* 'Config::from()' only extracts the config.
* Added 'Config::try_from()' for non-panicking extraction.
* 'Config' now knows the profile it was extracted from.
* The 'Config' provider sets a profile of 'Config.profile'.
* 'Rocket', 'Client', 'Fairings', implement 'Debug'.
* 'fairing::Info' implements 'Copy', 'Clone'.
* 'Fairings' keeps track of, logs attach fairings.
* 'Rocket::reconfigure()' was added to allow modifying a config.
Internally, the testing script was refactored to properly test the
codebase with the new changes. In particular, it no longer sets a rustc
'cfg' to avoid secret-key checking.
Resolves #1543.
Fixes #1564.
2021-03-09 08:07:43 +00:00
|
|
|
# let client = Client::debug(rocket).expect("valid rocket instance");
|
2021-08-20 02:43:05 +00:00
|
|
|
let mut response = client.get(uri!(hello)).dispatch();
|
2017-07-05 02:21:46 +00:00
|
|
|
|
|
|
|
assert_eq!(response.status(), Status::Ok);
|
|
|
|
assert_eq!(response.content_type(), Some(ContentType::Plain));
|
|
|
|
assert!(response.headers().get_one("X-Special").is_some());
|
2021-04-28 08:51:51 +00:00
|
|
|
assert_eq!(response.into_string().unwrap(), "Expected Body");
|
2017-07-05 02:21:46 +00:00
|
|
|
```
|
2017-04-17 02:48:59 +00:00
|
|
|
|
2017-07-05 02:21:46 +00:00
|
|
|
## Testing "Hello, world!"
|
2017-04-17 02:48:59 +00:00
|
|
|
|
2017-07-05 02:21:46 +00:00
|
|
|
To solidify an intuition for how Rocket applications are tested, we walk through
|
|
|
|
how to test the "Hello, world!" application below:
|
|
|
|
|
|
|
|
```rust
|
2020-02-15 11:43:47 +00:00
|
|
|
# #[macro_use] extern crate rocket;
|
|
|
|
|
2017-04-17 02:48:59 +00:00
|
|
|
#[get("/")]
|
|
|
|
fn hello() -> &'static str {
|
|
|
|
"Hello, world!"
|
|
|
|
}
|
|
|
|
|
2020-07-22 23:10:02 +00:00
|
|
|
#[launch]
|
2021-04-14 01:12:39 +00:00
|
|
|
fn rocket() -> _ {
|
2021-04-08 08:07:52 +00:00
|
|
|
rocket::build().mount("/", routes![hello])
|
2017-07-05 02:21:46 +00:00
|
|
|
}
|
2017-04-17 02:48:59 +00:00
|
|
|
```
|
|
|
|
|
2017-07-05 02:21:46 +00:00
|
|
|
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.
|
2017-04-17 02:48:59 +00:00
|
|
|
|
2017-07-05 02:21:46 +00:00
|
|
|
### Setting Up
|
|
|
|
|
|
|
|
First, we'll create a `test` module with the proper imports:
|
2017-04-17 02:48:59 +00:00
|
|
|
|
|
|
|
```rust
|
|
|
|
#[cfg(test)]
|
|
|
|
mod test {
|
|
|
|
use super::rocket;
|
2020-07-05 18:35:36 +00:00
|
|
|
use rocket::local::blocking::Client;
|
2017-07-05 02:21:46 +00:00
|
|
|
use rocket::http::Status;
|
2017-04-17 02:48:59 +00:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn hello_world() {
|
2020-02-15 11:43:47 +00:00
|
|
|
/* .. */
|
2017-04-17 02:48:59 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
2017-07-05 02:21:46 +00:00
|
|
|
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:
|
2017-04-17 02:48:59 +00:00
|
|
|
|
|
|
|
```rust
|
2017-07-05 02:21:46 +00:00
|
|
|
#[cfg(test)] mod tests;
|
2017-04-17 02:48:59 +00:00
|
|
|
```
|
|
|
|
|
2017-07-05 02:21:46 +00:00
|
|
|
### Testing
|
|
|
|
|
2020-01-08 16:03:05 +00:00
|
|
|
To test our "Hello, world!" application, we create a `Client` for our
|
2017-07-05 02:21:46 +00:00
|
|
|
`Rocket` instance. It's okay to use methods like `expect` and `unwrap` during
|
|
|
|
testing: we _want_ our tests to panic when something goes wrong.
|
2017-04-17 02:48:59 +00:00
|
|
|
|
|
|
|
```rust
|
2021-04-14 01:12:39 +00:00
|
|
|
# #[rocket::launch]
|
|
|
|
# fn rocket() -> _ {
|
Introduce statically-enforced 'Rocket' phasing.
The core 'Rocket' type is parameterized: 'Rocket<P: Phase>', where
'Phase' is a newly introduced, sealed marker trait. The trait is
implemented by three new marker types representing the three launch
phases: 'Build', 'Ignite', and 'Orbit'. Progression through these three
phases, in order, is enforced, as are the invariants guaranteed by each
phase. In particular, an instance of 'Rocket' is guaranteed to be in its
final configuration after the 'Build' phase and represent a running
local or public server in the 'Orbit' phase. The 'Ignite' phase serves
as an intermediate, enabling inspection of a finalized but stationary
instance. Transition between phases validates the invariants required
by the transition.
All APIs have been adjusted appropriately, requiring either an instance
of 'Rocket' in a particular phase ('Rocket<Build>', 'Rocket<Ignite>', or
'Rocket<Orbit>') or operating generically on a 'Rocket<P>'.
Documentation is also updated and substantially improved to mention
required and guaranteed invariants.
Additionally, this commit makes the following relevant changes:
* 'Rocket::ignite()' is now a public interface.
* 'Rocket::{build,custom}' methods can no longer panic.
* 'Launch' fairings are now 'ignite' fairings.
* 'Liftoff' fairings are always run, even in local mode.
* All 'ignite' fairings run concurrently at ignition.
* Launch logging occurs on launch, not any point prior.
* Launch log messages have improved formatting.
* A new launch error kind, 'Config', was added.
* A 'fairing::Result' type alias was introduced.
* 'Shutdown::shutdown()' is now 'Shutdown::notify()'.
Some internal changes were also introduced:
* Fairing 'Info' name for 'Templates' is now 'Templating'.
* Shutdown is implemented using 'tokio::sync::Notify'.
* 'Client::debug()' is used nearly universally in tests.
Resolves #1154.
Resolves #1136.
2021-04-14 02:26:45 +00:00
|
|
|
# rocket::build().configure(rocket::Config::debug_default())
|
Test 'secret_key' validation, now on pre-launch.
Prior to this commit, it was not possible to test Rocket crates in
production mode without setting a global secret key or bypassing secret
key checking - the testing script did the latter. The consequence is
that it became impossible to test secret key related failures because
the tests passed regardless.
This commit undoes this. As a consequence, all tests are now aware of
the difference between debug and release configurations, the latter of
which validates 'secret_key' by default. New 'Client::debug()' and
'Client::debug_with()' simplify creating an instance of 'Client' with
configuration in debug mode to avoid undesired test failures.
The summary of changes in this commit are:
* Config 'secret_key' success and failure are now tested.
* 'secret_key' validation was moved to pre-launch from 'Config:from()'.
* 'Config::from()' only extracts the config.
* Added 'Config::try_from()' for non-panicking extraction.
* 'Config' now knows the profile it was extracted from.
* The 'Config' provider sets a profile of 'Config.profile'.
* 'Rocket', 'Client', 'Fairings', implement 'Debug'.
* 'fairing::Info' implements 'Copy', 'Clone'.
* 'Fairings' keeps track of, logs attach fairings.
* 'Rocket::reconfigure()' was added to allow modifying a config.
Internally, the testing script was refactored to properly test the
codebase with the new changes. In particular, it no longer sets a rustc
'cfg' to avoid secret-key checking.
Resolves #1543.
Fixes #1564.
2021-03-09 08:07:43 +00:00
|
|
|
# }
|
2020-07-11 16:41:53 +00:00
|
|
|
# use rocket::local::blocking::Client;
|
2020-02-15 11:43:47 +00:00
|
|
|
|
2020-10-15 04:37:16 +00:00
|
|
|
let client = Client::tracked(rocket()).expect("valid rocket instance");
|
2017-04-17 02:48:59 +00:00
|
|
|
```
|
|
|
|
|
2017-07-05 02:21:46 +00:00
|
|
|
Then, we create a new `GET /` request and dispatch it, getting back our
|
|
|
|
application's response:
|
2017-04-17 02:48:59 +00:00
|
|
|
|
|
|
|
```rust
|
2021-08-20 02:43:05 +00:00
|
|
|
# use rocket::uri;
|
2021-04-14 01:12:39 +00:00
|
|
|
# #[rocket::launch]
|
|
|
|
# fn rocket() -> _ {
|
Introduce statically-enforced 'Rocket' phasing.
The core 'Rocket' type is parameterized: 'Rocket<P: Phase>', where
'Phase' is a newly introduced, sealed marker trait. The trait is
implemented by three new marker types representing the three launch
phases: 'Build', 'Ignite', and 'Orbit'. Progression through these three
phases, in order, is enforced, as are the invariants guaranteed by each
phase. In particular, an instance of 'Rocket' is guaranteed to be in its
final configuration after the 'Build' phase and represent a running
local or public server in the 'Orbit' phase. The 'Ignite' phase serves
as an intermediate, enabling inspection of a finalized but stationary
instance. Transition between phases validates the invariants required
by the transition.
All APIs have been adjusted appropriately, requiring either an instance
of 'Rocket' in a particular phase ('Rocket<Build>', 'Rocket<Ignite>', or
'Rocket<Orbit>') or operating generically on a 'Rocket<P>'.
Documentation is also updated and substantially improved to mention
required and guaranteed invariants.
Additionally, this commit makes the following relevant changes:
* 'Rocket::ignite()' is now a public interface.
* 'Rocket::{build,custom}' methods can no longer panic.
* 'Launch' fairings are now 'ignite' fairings.
* 'Liftoff' fairings are always run, even in local mode.
* All 'ignite' fairings run concurrently at ignition.
* Launch logging occurs on launch, not any point prior.
* Launch log messages have improved formatting.
* A new launch error kind, 'Config', was added.
* A 'fairing::Result' type alias was introduced.
* 'Shutdown::shutdown()' is now 'Shutdown::notify()'.
Some internal changes were also introduced:
* Fairing 'Info' name for 'Templates' is now 'Templating'.
* Shutdown is implemented using 'tokio::sync::Notify'.
* 'Client::debug()' is used nearly universally in tests.
Resolves #1154.
Resolves #1136.
2021-04-14 02:26:45 +00:00
|
|
|
# rocket::build().configure(rocket::Config::debug_default())
|
Test 'secret_key' validation, now on pre-launch.
Prior to this commit, it was not possible to test Rocket crates in
production mode without setting a global secret key or bypassing secret
key checking - the testing script did the latter. The consequence is
that it became impossible to test secret key related failures because
the tests passed regardless.
This commit undoes this. As a consequence, all tests are now aware of
the difference between debug and release configurations, the latter of
which validates 'secret_key' by default. New 'Client::debug()' and
'Client::debug_with()' simplify creating an instance of 'Client' with
configuration in debug mode to avoid undesired test failures.
The summary of changes in this commit are:
* Config 'secret_key' success and failure are now tested.
* 'secret_key' validation was moved to pre-launch from 'Config:from()'.
* 'Config::from()' only extracts the config.
* Added 'Config::try_from()' for non-panicking extraction.
* 'Config' now knows the profile it was extracted from.
* The 'Config' provider sets a profile of 'Config.profile'.
* 'Rocket', 'Client', 'Fairings', implement 'Debug'.
* 'fairing::Info' implements 'Copy', 'Clone'.
* 'Fairings' keeps track of, logs attach fairings.
* 'Rocket::reconfigure()' was added to allow modifying a config.
Internally, the testing script was refactored to properly test the
codebase with the new changes. In particular, it no longer sets a rustc
'cfg' to avoid secret-key checking.
Resolves #1543.
Fixes #1564.
2021-03-09 08:07:43 +00:00
|
|
|
# }
|
2021-08-20 02:43:05 +00:00
|
|
|
|
|
|
|
# #[rocket::get("/")]
|
|
|
|
# fn hello() -> &'static str { "Hello, world!" }
|
|
|
|
|
2020-07-11 16:41:53 +00:00
|
|
|
# use rocket::local::blocking::Client;
|
2020-10-15 04:37:16 +00:00
|
|
|
# let client = Client::tracked(rocket()).expect("valid rocket instance");
|
2021-08-20 02:43:05 +00:00
|
|
|
let mut response = client.get(uri!(hello)).dispatch();
|
2017-04-17 02:48:59 +00:00
|
|
|
```
|
|
|
|
|
2017-07-05 02:21:46 +00:00
|
|
|
Finally, we ensure that the response contains the information we expect it to.
|
|
|
|
Here, we want to ensure two things:
|
2017-04-17 02:48:59 +00:00
|
|
|
|
|
|
|
1. The status is `200 OK`.
|
|
|
|
2. The body is the string "Hello, world!".
|
|
|
|
|
2017-07-05 02:21:46 +00:00
|
|
|
We do this by checking the `Response` object directly:
|
2017-04-17 02:48:59 +00:00
|
|
|
|
|
|
|
```rust
|
2020-02-15 11:43:47 +00:00
|
|
|
# #[macro_use] extern crate rocket;
|
|
|
|
|
|
|
|
# #[get("/")]
|
|
|
|
# fn hello() -> &'static str { "Hello, world!" }
|
|
|
|
|
2020-07-11 16:41:53 +00:00
|
|
|
# use rocket::local::blocking::Client;
|
2020-02-15 11:43:47 +00:00
|
|
|
use rocket::http::{ContentType, Status};
|
2020-02-16 03:47:50 +00:00
|
|
|
#
|
2021-04-08 08:07:52 +00:00
|
|
|
# let rocket = rocket::build().mount("/", routes![hello]);
|
Test 'secret_key' validation, now on pre-launch.
Prior to this commit, it was not possible to test Rocket crates in
production mode without setting a global secret key or bypassing secret
key checking - the testing script did the latter. The consequence is
that it became impossible to test secret key related failures because
the tests passed regardless.
This commit undoes this. As a consequence, all tests are now aware of
the difference between debug and release configurations, the latter of
which validates 'secret_key' by default. New 'Client::debug()' and
'Client::debug_with()' simplify creating an instance of 'Client' with
configuration in debug mode to avoid undesired test failures.
The summary of changes in this commit are:
* Config 'secret_key' success and failure are now tested.
* 'secret_key' validation was moved to pre-launch from 'Config:from()'.
* 'Config::from()' only extracts the config.
* Added 'Config::try_from()' for non-panicking extraction.
* 'Config' now knows the profile it was extracted from.
* The 'Config' provider sets a profile of 'Config.profile'.
* 'Rocket', 'Client', 'Fairings', implement 'Debug'.
* 'fairing::Info' implements 'Copy', 'Clone'.
* 'Fairings' keeps track of, logs attach fairings.
* 'Rocket::reconfigure()' was added to allow modifying a config.
Internally, the testing script was refactored to properly test the
codebase with the new changes. In particular, it no longer sets a rustc
'cfg' to avoid secret-key checking.
Resolves #1543.
Fixes #1564.
2021-03-09 08:07:43 +00:00
|
|
|
# let client = Client::debug(rocket).expect("valid rocket instance");
|
2021-08-20 02:43:05 +00:00
|
|
|
# let mut response = client.get(uri!(hello)).dispatch();
|
2020-02-15 11:43:47 +00:00
|
|
|
|
2017-04-17 02:48:59 +00:00
|
|
|
assert_eq!(response.status(), Status::Ok);
|
2020-07-05 18:35:36 +00:00
|
|
|
assert_eq!(response.into_string(), Some("Hello, world!".into()));
|
2017-07-05 02:21:46 +00:00
|
|
|
```
|
2017-04-17 02:48:59 +00:00
|
|
|
|
2017-07-05 02:21:46 +00:00
|
|
|
That's it! Altogether, this looks like:
|
|
|
|
|
|
|
|
```rust
|
2020-02-15 11:43:47 +00:00
|
|
|
# #[macro_use] extern crate rocket;
|
Introduce statically-enforced 'Rocket' phasing.
The core 'Rocket' type is parameterized: 'Rocket<P: Phase>', where
'Phase' is a newly introduced, sealed marker trait. The trait is
implemented by three new marker types representing the three launch
phases: 'Build', 'Ignite', and 'Orbit'. Progression through these three
phases, in order, is enforced, as are the invariants guaranteed by each
phase. In particular, an instance of 'Rocket' is guaranteed to be in its
final configuration after the 'Build' phase and represent a running
local or public server in the 'Orbit' phase. The 'Ignite' phase serves
as an intermediate, enabling inspection of a finalized but stationary
instance. Transition between phases validates the invariants required
by the transition.
All APIs have been adjusted appropriately, requiring either an instance
of 'Rocket' in a particular phase ('Rocket<Build>', 'Rocket<Ignite>', or
'Rocket<Orbit>') or operating generically on a 'Rocket<P>'.
Documentation is also updated and substantially improved to mention
required and guaranteed invariants.
Additionally, this commit makes the following relevant changes:
* 'Rocket::ignite()' is now a public interface.
* 'Rocket::{build,custom}' methods can no longer panic.
* 'Launch' fairings are now 'ignite' fairings.
* 'Liftoff' fairings are always run, even in local mode.
* All 'ignite' fairings run concurrently at ignition.
* Launch logging occurs on launch, not any point prior.
* Launch log messages have improved formatting.
* A new launch error kind, 'Config', was added.
* A 'fairing::Result' type alias was introduced.
* 'Shutdown::shutdown()' is now 'Shutdown::notify()'.
Some internal changes were also introduced:
* Fairing 'Info' name for 'Templates' is now 'Templating'.
* Shutdown is implemented using 'tokio::sync::Notify'.
* 'Client::debug()' is used nearly universally in tests.
Resolves #1154.
Resolves #1136.
2021-04-14 02:26:45 +00:00
|
|
|
# use rocket::{Rocket, Build};
|
2020-02-15 11:43:47 +00:00
|
|
|
|
|
|
|
#[get("/")]
|
|
|
|
fn hello() -> &'static str {
|
|
|
|
"Hello, world!"
|
|
|
|
}
|
|
|
|
|
Introduce statically-enforced 'Rocket' phasing.
The core 'Rocket' type is parameterized: 'Rocket<P: Phase>', where
'Phase' is a newly introduced, sealed marker trait. The trait is
implemented by three new marker types representing the three launch
phases: 'Build', 'Ignite', and 'Orbit'. Progression through these three
phases, in order, is enforced, as are the invariants guaranteed by each
phase. In particular, an instance of 'Rocket' is guaranteed to be in its
final configuration after the 'Build' phase and represent a running
local or public server in the 'Orbit' phase. The 'Ignite' phase serves
as an intermediate, enabling inspection of a finalized but stationary
instance. Transition between phases validates the invariants required
by the transition.
All APIs have been adjusted appropriately, requiring either an instance
of 'Rocket' in a particular phase ('Rocket<Build>', 'Rocket<Ignite>', or
'Rocket<Orbit>') or operating generically on a 'Rocket<P>'.
Documentation is also updated and substantially improved to mention
required and guaranteed invariants.
Additionally, this commit makes the following relevant changes:
* 'Rocket::ignite()' is now a public interface.
* 'Rocket::{build,custom}' methods can no longer panic.
* 'Launch' fairings are now 'ignite' fairings.
* 'Liftoff' fairings are always run, even in local mode.
* All 'ignite' fairings run concurrently at ignition.
* Launch logging occurs on launch, not any point prior.
* Launch log messages have improved formatting.
* A new launch error kind, 'Config', was added.
* A 'fairing::Result' type alias was introduced.
* 'Shutdown::shutdown()' is now 'Shutdown::notify()'.
Some internal changes were also introduced:
* Fairing 'Info' name for 'Templates' is now 'Templating'.
* Shutdown is implemented using 'tokio::sync::Notify'.
* 'Client::debug()' is used nearly universally in tests.
Resolves #1154.
Resolves #1136.
2021-04-14 02:26:45 +00:00
|
|
|
|
|
|
|
# /*
|
|
|
|
#[launch]
|
|
|
|
# */
|
|
|
|
fn rocket() -> Rocket<Build> {
|
2021-04-08 08:07:52 +00:00
|
|
|
rocket::build().mount("/", routes![hello])
|
2020-02-15 11:43:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
# /*
|
2017-07-05 02:21:46 +00:00
|
|
|
#[cfg(test)]
|
2020-02-15 11:43:47 +00:00
|
|
|
# */
|
2017-07-05 02:21:46 +00:00
|
|
|
mod test {
|
|
|
|
use super::rocket;
|
2020-07-05 18:35:36 +00:00
|
|
|
use rocket::local::blocking::Client;
|
2017-07-05 02:21:46 +00:00
|
|
|
use rocket::http::Status;
|
|
|
|
|
2020-02-15 11:43:47 +00:00
|
|
|
# /*
|
2017-07-05 02:21:46 +00:00
|
|
|
#[test]
|
2020-02-15 11:43:47 +00:00
|
|
|
# */ pub
|
2017-07-05 02:21:46 +00:00
|
|
|
fn hello_world() {
|
Test 'secret_key' validation, now on pre-launch.
Prior to this commit, it was not possible to test Rocket crates in
production mode without setting a global secret key or bypassing secret
key checking - the testing script did the latter. The consequence is
that it became impossible to test secret key related failures because
the tests passed regardless.
This commit undoes this. As a consequence, all tests are now aware of
the difference between debug and release configurations, the latter of
which validates 'secret_key' by default. New 'Client::debug()' and
'Client::debug_with()' simplify creating an instance of 'Client' with
configuration in debug mode to avoid undesired test failures.
The summary of changes in this commit are:
* Config 'secret_key' success and failure are now tested.
* 'secret_key' validation was moved to pre-launch from 'Config:from()'.
* 'Config::from()' only extracts the config.
* Added 'Config::try_from()' for non-panicking extraction.
* 'Config' now knows the profile it was extracted from.
* The 'Config' provider sets a profile of 'Config.profile'.
* 'Rocket', 'Client', 'Fairings', implement 'Debug'.
* 'fairing::Info' implements 'Copy', 'Clone'.
* 'Fairings' keeps track of, logs attach fairings.
* 'Rocket::reconfigure()' was added to allow modifying a config.
Internally, the testing script was refactored to properly test the
codebase with the new changes. In particular, it no longer sets a rustc
'cfg' to avoid secret-key checking.
Resolves #1543.
Fixes #1564.
2021-03-09 08:07:43 +00:00
|
|
|
# /*
|
2020-10-15 04:37:16 +00:00
|
|
|
let client = Client::tracked(rocket()).expect("valid rocket instance");
|
Test 'secret_key' validation, now on pre-launch.
Prior to this commit, it was not possible to test Rocket crates in
production mode without setting a global secret key or bypassing secret
key checking - the testing script did the latter. The consequence is
that it became impossible to test secret key related failures because
the tests passed regardless.
This commit undoes this. As a consequence, all tests are now aware of
the difference between debug and release configurations, the latter of
which validates 'secret_key' by default. New 'Client::debug()' and
'Client::debug_with()' simplify creating an instance of 'Client' with
configuration in debug mode to avoid undesired test failures.
The summary of changes in this commit are:
* Config 'secret_key' success and failure are now tested.
* 'secret_key' validation was moved to pre-launch from 'Config:from()'.
* 'Config::from()' only extracts the config.
* Added 'Config::try_from()' for non-panicking extraction.
* 'Config' now knows the profile it was extracted from.
* The 'Config' provider sets a profile of 'Config.profile'.
* 'Rocket', 'Client', 'Fairings', implement 'Debug'.
* 'fairing::Info' implements 'Copy', 'Clone'.
* 'Fairings' keeps track of, logs attach fairings.
* 'Rocket::reconfigure()' was added to allow modifying a config.
Internally, the testing script was refactored to properly test the
codebase with the new changes. In particular, it no longer sets a rustc
'cfg' to avoid secret-key checking.
Resolves #1543.
Fixes #1564.
2021-03-09 08:07:43 +00:00
|
|
|
# */
|
|
|
|
# let client = Client::debug(rocket()).expect("valid rocket instance");
|
2021-08-20 02:43:05 +00:00
|
|
|
let mut response = client.get(uri!(super::hello)).dispatch();
|
2017-07-05 02:21:46 +00:00
|
|
|
assert_eq!(response.status(), Status::Ok);
|
Introduce statically-enforced 'Rocket' phasing.
The core 'Rocket' type is parameterized: 'Rocket<P: Phase>', where
'Phase' is a newly introduced, sealed marker trait. The trait is
implemented by three new marker types representing the three launch
phases: 'Build', 'Ignite', and 'Orbit'. Progression through these three
phases, in order, is enforced, as are the invariants guaranteed by each
phase. In particular, an instance of 'Rocket' is guaranteed to be in its
final configuration after the 'Build' phase and represent a running
local or public server in the 'Orbit' phase. The 'Ignite' phase serves
as an intermediate, enabling inspection of a finalized but stationary
instance. Transition between phases validates the invariants required
by the transition.
All APIs have been adjusted appropriately, requiring either an instance
of 'Rocket' in a particular phase ('Rocket<Build>', 'Rocket<Ignite>', or
'Rocket<Orbit>') or operating generically on a 'Rocket<P>'.
Documentation is also updated and substantially improved to mention
required and guaranteed invariants.
Additionally, this commit makes the following relevant changes:
* 'Rocket::ignite()' is now a public interface.
* 'Rocket::{build,custom}' methods can no longer panic.
* 'Launch' fairings are now 'ignite' fairings.
* 'Liftoff' fairings are always run, even in local mode.
* All 'ignite' fairings run concurrently at ignition.
* Launch logging occurs on launch, not any point prior.
* Launch log messages have improved formatting.
* A new launch error kind, 'Config', was added.
* A 'fairing::Result' type alias was introduced.
* 'Shutdown::shutdown()' is now 'Shutdown::notify()'.
Some internal changes were also introduced:
* Fairing 'Info' name for 'Templates' is now 'Templating'.
* Shutdown is implemented using 'tokio::sync::Notify'.
* 'Client::debug()' is used nearly universally in tests.
Resolves #1154.
Resolves #1136.
2021-04-14 02:26:45 +00:00
|
|
|
assert_eq!(response.into_string().unwrap(), "Hello, world!");
|
2017-07-05 02:21:46 +00:00
|
|
|
}
|
|
|
|
}
|
2020-02-15 11:43:47 +00:00
|
|
|
|
|
|
|
# fn main() { test::hello_world(); }
|
2017-04-17 02:48:59 +00:00
|
|
|
```
|
|
|
|
|
2017-07-05 02:21:46 +00:00
|
|
|
The tests can be run with `cargo test`. You can find the full source code to
|
2022-06-10 18:21:15 +00:00
|
|
|
[this example on GitHub](@git/v0.5/examples/testing).
|
2017-04-17 02:48:59 +00:00
|
|
|
|
2020-07-08 16:20:12 +00:00
|
|
|
## 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`
|
2020-07-09 01:36:28 +00:00
|
|
|
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] uses an `async` barrier to demonstrate such
|
|
|
|
a case. For more information, see the [`rocket::local`] and
|
|
|
|
[`rocket::local::asynchronous`] documentation.
|
2020-07-08 16:20:12 +00:00
|
|
|
|
2022-06-10 18:21:15 +00:00
|
|
|
[`rocket::local`]: @api/v0.5/rocket/local/index.html
|
|
|
|
[`rocket::local::asynchronous`]: @api/v0.5/rocket/local/asynchronous/index.html
|
|
|
|
[`async_required` `testing` example]: @git/v0.5/examples/testing/src/async_required.rs
|
2020-07-08 16:20:12 +00:00
|
|
|
|
2017-04-17 02:48:59 +00:00
|
|
|
## Codegen Debug
|
|
|
|
|
2017-07-05 02:21:46 +00:00
|
|
|
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:
|
2017-04-17 02:48:59 +00:00
|
|
|
|
2020-02-15 11:43:47 +00:00
|
|
|
```sh
|
2017-04-17 02:48:59 +00:00
|
|
|
ROCKET_CODEGEN_DEBUG=1 cargo build
|
|
|
|
```
|
|
|
|
|
2017-07-05 02:21:46 +00:00
|
|
|
During compilation, you should see output like:
|
2017-04-17 02:48:59 +00:00
|
|
|
|
2020-02-15 11:43:47 +00:00
|
|
|
```rust,ignore
|
2018-10-22 21:47:35 +00:00
|
|
|
note: emitting Rocket code generation debug output
|
2021-05-22 23:42:27 +00:00
|
|
|
--> examples/hello/src/main.rs:14:1
|
2021-03-20 01:09:13 +00:00
|
|
|
|
|
|
|
|
14 | #[get("/world")]
|
|
|
|
| ^^^^^^^^^^^^^^^^
|
|
|
|
|
|
|
|
|
= note:
|
2021-04-29 12:19:24 +00:00
|
|
|
impl world {
|
|
|
|
fn into_info(self) -> rocket::StaticRouteInfo {
|
2021-03-20 01:09:13 +00:00
|
|
|
fn monomorphized_function<'_b>(
|
|
|
|
__req: &'_b rocket::request::Request<'_>,
|
|
|
|
__data: rocket::data::Data,
|
2021-04-29 12:19:24 +00:00
|
|
|
) -> ::rocket::route::BoxFuture<'_b> {
|
2021-03-20 01:09:13 +00:00
|
|
|
::std::boxed::Box::pin(async move {
|
|
|
|
let ___responder = world();
|
2021-04-29 12:19:24 +00:00
|
|
|
::rocket::handler::Outcome::from(__req, ___responder)
|
2021-03-20 01:09:13 +00:00
|
|
|
})
|
|
|
|
}
|
2021-04-29 12:19:24 +00:00
|
|
|
|
|
|
|
::rocket::StaticRouteInfo {
|
2021-03-20 01:09:13 +00:00
|
|
|
name: "world",
|
|
|
|
method: ::rocket::http::Method::Get,
|
|
|
|
path: "/world",
|
|
|
|
handler: monomorphized_function,
|
|
|
|
format: ::std::option::Option::None,
|
|
|
|
rank: ::std::option::Option::None,
|
2021-04-29 12:19:24 +00:00
|
|
|
sentinels: sentinels![&'static str],
|
2021-03-20 01:09:13 +00:00
|
|
|
}
|
|
|
|
}
|
2018-10-22 21:47:35 +00:00
|
|
|
}
|
2017-04-17 02:48:59 +00:00
|
|
|
```
|
|
|
|
|
2017-07-05 02:21:46 +00:00
|
|
|
This corresponds to the facade request handler Rocket has generated for the
|
|
|
|
`hello` route.
|