httptest/
lib.rs

1/*!
2# httptest
3
4Provide convenient mechanism for testing http clients against a locally
5running http server. The typical usage is as follows:
6
7* Start a server
8* Configure the server by adding expectations
9* Test your http client by making requests to the server
10* On Drop the server verifies all expectations were met.
11
12## Example Test
13
14```
15# async fn foo() {
16use http_body_util::Full;
17use hyper_util::client::legacy::Client;
18use httptest::{Server, Expectation, matchers::*, responders::*};
19
20// Starting a logger within the test can make debugging a failed test
21// easier. The mock http server will log::debug every request and response
22// received along with what, if any, matcher was found for the request. When
23// env_logger is initialized running the test with `RUST_LOG=httptest=debug
24// cargo test` can provide that information on stderr.
25let _ = pretty_env_logger::try_init();
26
27// Start a server running on a local ephemeral port.
28let server = Server::run();
29// Configure the server to expect a single GET /foo request and respond
30// with a 200 status code.
31server.expect(
32    Expectation::matching(request::method_path("GET", "/foo"))
33    .respond_with(status_code(200)),
34);
35
36// The server provides server.addr() that returns the address of the
37// locally running server, or more conveniently provides a server.url() method
38// that gives a fully formed http url to the provided path.
39let url = server.url("/foo");
40let client = Client::builder(hyper_util::rt::TokioExecutor::new()).build_http::<Full<bytes::Bytes>>();
41// Issue the GET /foo to the server.
42let resp = client.get(url).await.unwrap();
43
44// assert the response has a 200 status code.
45assert!(resp.status().is_success());
46
47// on Drop the server will assert all expectations have been met and will
48// panic if not.
49# }
50```
51
52# Server behavior
53
54Typically the server is started by calling
55[Server::run](struct.Server.html#method.run). It starts without any
56expectations configured.
57
58Expectations are added by calling
59[Server::expect](struct.Server.html#method.expect). Every invocation of
60expect appends a new expectation onto the list. Expectations are only removed
61from the server on Drop or when
62[Server::verify_and_clear](struct.Server.html#method.verify_and_clear) is
63invoked. This guarantees that all expectations are always verified.
64
65Expectations consist of:
66* A matcher that determines which requests match this expectation
67* The number of times a request matching this expectation is expected to be received
68* A responder that indicates how the server should respond to the request.
69
70When the server receives a request it iterates over all expectations in the
71*reverse* order they have been added. When it reaches an expectation that
72matches the request, it increments the hit count on that expectation and
73verifies it has not exceeded it's expected number of requests. If the
74limit has been exceeded a 500 error is returned, if the limit has not been
75exceeded it uses the expectation's responder to respond to the request. If the
76request does not match any expectation a 500 error is returned.
77
78When the server is Dropped it:
79* Stops running
80* Panics if
81  * any expectation did not receive the expected number of requests
82  * a request was received that did not match any expectation
83
84Clients can determine the address and port the server is reachable at using
85[Server::addr](struct.Server.html#method.addr), or the helper methods
86[Server::url](struct.Server.html#method.url) and
87[Server::url_str](struct.Server.html#method.url_str).
88
89## Server Pooling
90
91Typical usage would use [Server::run](struct.Server.html#method.run) early in
92each test case and have the Drop implementation at the end of the test assert
93all expectations were met. This runs a separate server for each test. Rust's
94test harness starts a separate thread for each test within a test-suite so
95the machine running the test would likely end up running a server for each
96#\[test\] function concurrently. For large test suites this could cause machine
97wide resources (like tcp ports) to become scarce. To address this you could
98use the --test-threads flag on the test-harness to limit the number of
99threads running, or alternatively you could use a global
100[ServerPool](struct.ServerPool.html) instance.
101
102The [ServerPool](struct.ServerPool.html) allows limiting the number of
103servers that can be running concurrently while still allowing test cases to
104function independently.
105
106### ServerPool example
107
108```
109# use httptest::ServerPool;
110// Create a server pool that will create at most 2 servers.
111static SERVER_POOL: ServerPool = ServerPool::new(2);
112
113#[test]
114fn test1() {
115    let server = SERVER_POOL.get_server();
116    server.expect(Expectation::matching(any()).respond_with(status_code(200)));
117
118    // Send requests to server
119    // Server will assert expectations on drop.
120}
121
122#[test]
123fn test2() {
124    let server = SERVER_POOL.get_server();
125    server.expect(Expectation::matching(any()).respond_with(status_code(200)));
126
127    // Send requests to server
128    // Server will assert expectations on drop.
129}
130
131#[test]
132fn test3() {
133    let server = SERVER_POOL.get_server();
134    server.expect(Expectation::matching(any()).respond_with(status_code(200)));
135
136    // Send requests to server
137    // Server will assert expectations on drop.
138}
139```
140
141This is almost identical to tests without pooling, the only addition is
142creating a static ServerPool instance, and using `SERVER_POOL.get_server()`
143instead of `Server::run()`. This will effectively limit the amount of
144concurrency of the test suite to two tests at a time. The first two tests to execute
145`get_server()` will be handed servers without blocking, the 3rd test will block
146in `get_server()` until one of the first 2 tests complete.
147
148# Defining Expectations
149
150Every expecation defines a request matcher, a defintion of the number of
151times it's expected to be called, and what it should respond with.
152
153### Expectation example
154
155```
156use httptest::{Expectation, matchers::*, responders::*};
157
158// Define an Expectation that matches any request to path /foo, expects to
159// receive at least 1 such request, and responds with a 200 response.
160Expectation::matching(request::path("/foo"))
161    .times(1..)
162    .respond_with(status_code(200));
163```
164
165## Request Matchers
166
167Defining which request an expecation matches is done in a composable manner
168using a [Matcher](matchers/trait.Matcher.html) trait. The `Matcher` trait is
169generic over an input type and defines a single method `matches` that returns
170a boolean if the input matches.
171
172A request matcher is any `Matcher` that accepts a
173`http::Request<hyper::body::Bytes>` as input. A true result indicates the
174request matches.
175
176With that understanding we can discuss how to easily define a request
177matcher. There are a variety of pre-defined matchers within the
178[matchers](matchers/index.html) module. These matchers can be composed
179together to define the values you want to match. The matchers fall into two
180categories. Some of the matchers extract a value from the input type and pass
181it to another matcher, other matchers accept an input type and return a bool.
182These primitives provide an easy and flexible way to define custom logic.
183
184### Matcher examples
185
186```
187// pull all the predefined matchers into our namespace.
188use httptest::matchers::*;
189
190// &str, String, and &[u8] all implement matchers that test for equality.
191// All of these matchers return true when the input equals "/foo"
192let mut m = eq("/foo");
193let mut m = "/foo";
194let mut m = "/foo".to_string();
195let mut m = &b"/foo"[..];
196
197// A mapper that returns true when the input matches the regex "(foo|bar).*"
198let mut m = matches("(foo|bar).*");
199
200// A request matcher that matches a request to path "/foo"
201let mut m = request::path("/foo");
202
203// A request matcher that matches a POST request
204let mut m = request::method("POST");
205
206// A request matcher that matches a POST with a path that matches the regex 'foo.*'
207let mut m = all_of![
208    request::method("POST"),
209    request::path(matches("foo.*")),
210];
211
212# // Allow type inference to determine the request type.
213# ExecutionContext::evaluate(&mut m, &http::Request::get("/").body("").unwrap());
214```
215
216## Times
217
218Each expectation defines how many times a matching request is expected to
219be received. The default is exactly once. The ExpectationBuilder provides a
220[times](struct.ExpectationBuilder.html#method.times) method to specify the
221number of requests expected.
222
223```
224# use httptest::{Expectation, matchers::any, responders::status_code};
225// Expect exactly one request
226Expectation::matching(any())
227    .respond_with(status_code(200));
228
229// Expect exactly two requests
230Expectation::matching(any())
231    .times(2)
232    .respond_with(status_code(200));
233
234// Expect at least 2 requests
235Expectation::matching(any())
236    .times(2..)
237    .respond_with(status_code(200));
238
239// Expect at most 2 requests
240Expectation::matching(any())
241    .times(..2)
242    .respond_with(status_code(200));
243
244// Expect between 2 and 5 requests
245Expectation::matching(any())
246    .times(2..6)
247    .respond_with(status_code(200));
248
249// Expect between 2 and 5 requests
250Expectation::matching(any())
251    .times(2..=5)
252    .respond_with(status_code(200));
253
254// Expect any number of requests.
255Expectation::matching(any())
256    .times(..)
257    .respond_with(status_code(200));
258```
259
260The server will respond to any requests that violate the times restriction with
261a 500 status code and the server will subsequently panic on Drop.
262
263## Responder
264
265Responders define how the server will respond to a matched request. There
266are a number of implemented responders within the responders module. In
267addition to the predefined responders you can provide any
268`http::Response` with a body that can be cloned or implement your own
269Responder.
270
271### Responder example
272
273```
274use httptest::responders::*;
275
276// respond with a successful 200 status code.
277status_code(200);
278
279// respond with a 404 page not found and a custom header.
280status_code(404).append_header("X-My-Hdr", "my hdr val");
281
282// respond with a successful 200 status code and body.
283status_code(200).body("my body");
284
285// respond with a json encoded body and custom header.
286json_encoded(serde_json::json!({
287    "my_key": 100,
288    "my_key2": [1, 2, "foo", 99],
289})).append_header("X-My-Hdr", "my hdr val");
290
291// respond with a url encoded body (foo=bar&baz=bat)
292url_encoded(&[
293    ("foo", "bar"),
294    ("baz", "bat")
295]);
296
297// alternate between responding with a 200 and a 404.
298cycle![
299    status_code(200),
300    status_code(404),
301];
302```
303!*/
304
305#![deny(missing_docs)]
306
307/// true if all the provided matchers return true.
308///
309/// The macro exists to conveniently box a list of matchers and put them into a
310/// `Vec<Box<dyn Matcher>>`. The translation is:
311///
312/// `all_of![a, b] => all_of(vec![Box::new(a), Box::new(b)])`
313#[macro_export]
314macro_rules! all_of {
315    ($($x:expr),*) => ($crate::matchers::all_of($crate::vec_of_boxes![$($x),*]));
316    ($($x:expr,)*) => ($crate::all_of![$($x),*]);
317}
318
319/// true if any of the provided matchers return true.
320///
321/// The macro exists to conveniently box a list of matchers and put them into a
322/// `Vec<Box<dyn Matcher>>`. The translation is:
323///
324/// `any_of![a, b] => any_of(vec![Box::new(a), Box::new(b)])`
325#[macro_export]
326macro_rules! any_of {
327    ($($x:expr),*) => ($crate::matchers::any_of($crate::vec_of_boxes![$($x),*]));
328    ($($x:expr,)*) => ($crate::any_of![$($x),*]);
329}
330
331/// a Responder that cycles through a list of responses.
332///
333/// The macro exists to conveniently box a list of responders and put them into a
334/// `Vec<Box<dyn Responder>>`. The translation is:
335///
336/// `cycle![a, b] => cycle(vec![Box::new(a), Box::new(b)])`
337#[macro_export]
338macro_rules! cycle {
339    ($($x:expr),*) => ($crate::responders::cycle($crate::vec_of_boxes![$($x),*]));
340    ($($x:expr,)*) => ($crate::cycle![$($x),*]);
341}
342
343// hidden from docs because it's an implementation detail of the above macros.
344#[doc(hidden)]
345#[macro_export]
346macro_rules! vec_of_boxes {
347    ($($x:expr),*) => (std::vec![$(std::boxed::Box::new($x)),*]);
348    ($($x:expr,)*) => ($crate::vec_of_boxes![$($x),*]);
349}
350
351// re-exports of types from dependent crates that show up in the public api.
352pub use bytes;
353pub use http;
354
355mod into_times;
356pub mod matchers;
357pub mod responders;
358mod server;
359mod server_pool;
360
361pub use into_times::IntoTimes;
362pub use server::{Expectation, ExpectationBuilder, Server, ServerBuilder};
363pub use server_pool::{ServerHandle, ServerPool};