[−][src]Crate httptest
httptest
Provide convenient mechanism for testing http clients against a locally running http server. The typical usage is as follows:
- Start a server
- Configure the server by adding expectations
- Test your http client by making requests to the server
- On Drop the server verifies all expectations were met.
Example Test
use httptest::{Server, Expectation, Times, mappers::*, responders::*}; // Start a server running on a local ephemeral port. let server = Server::run(); // Configure the server to expect a single GET /foo request and respond // with a 200 status code. server.expect( Expectation::matching(all_of![ request::method(eq("GET")), request::path(eq("/foo")) ]) .times(Times::Exactly(1)) .respond_with(status_code(200)), ); // The server provides server.addr() that returns the address of the // locally running server, or more conveniently provides a server.url() method // that gives a fully formed http url to the provided path. let url = server.url("/foo"); let client = hyper::Client::new(); // Issue the GET /foo to the server. let resp = client.get(url).await.unwrap(); // Use response matchers to assert the response has a 200 status code. assert!(response::status_code(eq(200)).matches(&resp)); // on Drop the server will assert all expectations have been met and will // panic if not.
Server behavior
The Server is started with run().
The server will run in a background thread until it's dropped. Once dropped it will assert that every configured expectation has been met or will panic. You can also use verify_and_clear() to assert and clear the expectations while keeping the server running.
addr() will return the address the server is listening on.
url() will construct a fully formed http url to the path provided i.e.
server.url("/foo?key=value") == "https://<server_addr>/foo?key=value"
.
Defining Expecations
Every expecation defines a request matcher, a defintion of the number of times it's expected to be called, and what it should respond with.
Expectation example
use httptest::{Expectation, mappers::*, responders::*, Times}; // Define an Expectation that matches any request to path /foo, expects to // receive at least 1 such request, and responds with a 200 response. Expectation::matching(request::path(eq("/foo"))) .times(Times::AtLeast(1)) .respond_with(status_code(200));
Request Matchers
Defining which request an expecation matches is done in a composable manner
using a series of traits. The core of which is
Mapper. The Mapper
trait is generic
over an input type, has an associated Out
type, and defines a single method
map
that converts from a shared reference of the input type to the Out
type.
There's a specialized form of a Mapper where the Out
type is a boolean.
Any Mapper
that outputs a boolean value is considered a Matcher and
implements the Matcher trait as well. The
Matcher trait simply provides a matches
method.
A request matcher is any Matcher
that takes accepts a
http::Request<hyper::body::Bytes>
as input.
With that understanding we can discuss how to easily define a request matcher. There are a variety of pre-defined mappers within the mappers module. These mappers can be composed together to define the values you want to match. The mappers fall into two categories. Some of the mappers extract a value from the input type and pass it to another mapper, other mappers accept an input type and return a bool. These primitives provide an easy and flexible way to define custom logic.
Matcher examples
// pull all the predefined mappers into our namespace. use httptest::mappers::*; // A mapper that returns true when the input equals "/foo" let mut m = eq("/foo"); // A mapper that returns true when the input matches the regex "(foo|bar).*" let mut m = matches("(foo|bar).*"); // A request matcher that matches a request to path "/foo" let mut m = request::path(eq("/foo")); // A request matcher that matches a POST request let mut m = request::method(eq("POST")); // A request matcher that matches a POST with a path that matches the regex 'foo.*' let mut m = all_of![ request::method(eq("POST")), request::path(matches("foo.*")), ];
Times
Each expectation defines how many times a matching request is expected to
be received. The Times enum defines the possibility.
Times::Exactly(1)
is the default value of an Expectation
if one is not
specified with the
times() method.
The server will respond to any requests that violate the times request with a 500 status code and the server will subsequently panic on Drop.
Responder
Responders define how the server will respond to a matched request. There
are a number of implemented responders within the responders module. In
addition to the predefined responders you can provide any
http::Response
with a body that can be cloned or implement your own
Responder.
Responder example
use httptest::responders::*; // respond with a successful 200 status code. status_code(200); // respond with a 404 page not found. status_code(404); // respond with a json encoded body. json_encoded(serde_json::json!({ "my_key": 100, "my_key2": [1, 2, "foo", 99], })); // alternate between responding with a 200 and a 404. cycle![ status_code(200), status_code(404), ];
Modules
mappers | Mapper implementations. |
responders | Responder implementations. |
Macros
all_of | true if all the provided matchers return true. |
any_of | true if any of the provided matchers return true. |
cycle | a Responder that cycles through a list of responses. |
Structs
Expectation | An expectation to be asserted by the server. |
ExpectationBuilder | Define expectations using a builder pattern. |
Server | The Server |
Enums
Times | How many requests should an expectation receive. |