Custom Responses
This section explains how to configure the mock server to generate dynamic responses.
Custom responses give you full control over the returned status, headers, and body at runtime.
respond_with
Sets a dynamic responder that is invoked for every request matching the when
conditions. The provided closure receives the full HttpMockRequest
and must
fully determine the HttpMockResponse
(status, headers, and body).
This is ideal when the reply depends on request details (path, headers, body), or when you need stateful behavior across calls (e.g., counters, cycling codes).
Important: Dynamic responders are only supported by the local server. They are not supported by remote/standalone servers and this method will panic when used against them.
Parameters
f
: A response generator closure that is executed on the mock server’s request handling thread for each match.
Behavior
- When set, this responder overrides any static configuration on
Then
(e.g., previously set status/body/headers) for matching requests other than the configured delay using thedelay
method. - The closure may capture shared state. If you mutate shared data, use a
synchronization primitive (
Mutex
,RwLock
, or atomics). This is required because the responder runs on the mock server’s request-handling thread, which is distinct from the test thread.
Returns
Returns self
for continued method chaining on Then
.
Example
A minimal dynamic responder that mirrors the request body value back to the client:
use httpmock::{MockServer, HttpMockRequest, HttpMockResponse};use reqwest::blocking::Client;
let server = MockServer::start();
let mock = server.mock(|when, then| { when.path("/echo"); then.respond_with(|req: &HttpMockRequest| { // Echo the received request body back in the response body. let echoed_body = req.body().to_string();
HttpMockResponse::builder() .status(200) .body(echoed_body) .build() });});
let res = Client::new() .post(format!("{}/echo", server.base_url())) .body("Hello, world!") .send() .unwrap();
mock.assert();
assert_eq!(res.status(), 200);assert_eq!(res.text().unwrap(), "Hello, world!");
Example
Stateful dynamic responder that increments the status code on each call:
use httpmock::{MockServer, HttpMockRequest, HttpMockResponse};use reqwest::blocking::Client;use std::sync::Mutex;
// Arrangelet server = MockServer::start();
// Shared counter used inside the responder closure.// Use a Mutex/RwLock/atomic because the closure runs on the server thread.let call_count = Mutex::new(0);
let mock = server.mock(|when, then| { when.path("/hello"); then.respond_with(move |_req: &HttpMockRequest| { let mut count = call_count.lock().unwrap(); *count += 1;
HttpMockResponse::builder() .status(200 + *count) // 201, 202, 203, ... .build() });});
// Actlet client = Client::new();let response1 = client.get(format!("{}/hello", server.base_url())).send().unwrap();let response2 = client.get(format!("{}/hello", server.base_url())).send().unwrap();let response3 = client.get(format!("{}/hello", server.base_url())).send().unwrap();
// Assertmock.assert_calls(3);assert_eq!(response1.status(), 201);assert_eq!(response2.status(), 202);assert_eq!(response3.status(), 203);
HTTP Crate Integration
Both HttpMockRequest
and HttpMockResponse
implement conversion traits
to and from types of the http
crate.
This means you can seamlessly integrate with other HTTP utilities or middleware
that operate on http::Request
and http::Response
.
use httpmock::{MockServer, HttpMockRequest, HttpMockResponse};use reqwest::blocking::Client;
let server = MockServer::start();
let mock = server.mock(|when, then| { when.method("POST").path("/echo"); then.respond_with(|req: &HttpMockRequest| { // Convert the HttpMockRequest to an `http` crate Request (with body) let http_req: http::Request<String> = req.into();
// Build an `http` crate Response (auto-converted to HttpMockResponse) http::Response::builder() .status(200) .header("content-type", "text/plain") .body(format!("Echo from {}: {}", http_req.uri().path(), http_req.body())) .unwrap() .into() });});
let res = Client::new() .post(format!("{}/echo", server.base_url())) .body("Hello from client!") .send() .unwrap();
mock.assert();assert_eq!(res.status(), 200);assert_eq!(res.text().unwrap(), "Echo from /echo: Hello from client!");
Notes
- Avoid long-running or blocking work inside the responder; it runs on the request handling path and will delay responses.
- If you need to combine static defaults with dynamic tweaks, compute them inside
the closure (e.g., start from
HttpMockResponse::builder()
and adjust as needed).