Skip to content

Network Delay

This section describes functions designed to simulate network issues, such as latency (delay).

delay

Sets a delay for the mock server response.

This method configures the server to wait for a specified duration before sending a response, which can be useful for testing timeout scenarios or asynchronous operations.

Parameters

  • duration: The length of the delay as a std::time::Duration.

Returns

Returns self to allow chaining of method calls on the Mock object.

Panics

Panics if the specified duration results in a delay that cannot be represented as a 64-bit unsigned integer of milliseconds (more than approximately 584 million years).

Example

Demonstrates setting a 3-second delay for a request to the path /delay.

use std::time::{SystemTime, Duration};
use httpmock::prelude::*;
use reqwest::blocking::Client;
// Arrange
let _ = env_logger::try_init();
let start_time = SystemTime::now();
let three_seconds = Duration::from_secs(3);
let server = MockServer::start();
// Configure the mock
let mock = server.mock(|when, then| {
when.path("/delay");
then.status(200)
.delay(three_seconds);
});
// Act
let response = Client::new()
.get(server.url("/delay"))
.send()
.unwrap();
// Assert
mock.assert();
assert!(start_time.elapsed().unwrap() >= three_seconds);

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 the delay 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;
// Arrange
let 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()
});
});
// Act
let 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();
// Assert
mock.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).