Testing
Tests need no server, no port and no browser. app().test("") builds your app on a fresh in-memory database, migrated and seeded, and testing::Client browses it like a visitor. Run them with cargo test.
A route test#
use rustclamp::web::testing::Client;
fn app(env: &str) -> rustclamp::web::Router {
my_site::app().test(env).0
}
#[test]
fn health_returns_json() {
let routes = app("");
let response = Client::new(&routes).get("/api/health");
assert_eq!((response.status, response.content_type), (200, "application/json"));
}
test returns the database as well, let (routes, db) = my_site::app().test(""), so a test can check what a form stored.
Forms, sessions and CSRF#
The client keeps the cookies it is given, so the session carries over between requests. post sends the CSRF token of the last page it fetched, as a browser submitting that page's form would, so open the page first:
let client = Client::new(&routes);
client.get("/contact");
let sent = client.post("/contact", &[("name", "Neo"), ("email", "[email protected]")]);
assert_eq!((sent.status, sent.location()), (302, Some("/contact")));
assert!(client.get("/contact").see("Thanks, your message was received."));
// No token: proves the group really has csrf().
assert_eq!(client.post_raw("/contact", &[("name", "Neo")]).status, 419);
What to assert#
response.status, response.content_type | The status and type |
response.see("text") | The body contains text, as written (so check escaping with see("<b>")) |
response.location(), response.header("retry-after") | Headers |
response.body_text() | The whole body |
Settings in tests#
Pass .env lines to test instead of reading the machine's .env. Rate limits count per client, so from sets the address:
#[test]
fn api_is_throttled_per_client() {
let routes = app("API_PER_MINUTE=1\n");
let client = Client::new(&routes).from("10.0.0.1");
assert_eq!(client.get("/api/health").status, 200);
assert_eq!(client.get("/api/health").status, 429);
}
Without the app#
A bare Router answers a Request directly, for testing a handler or middleware alone: routes.handle(&Request::get("/search?q=clamp")). Build requests with Request::post(path).with_body("a=1"), .with_header(...) and .with_peer(ip).