Recording
httpmock
provides functionality to record both requests to third-party services and their responses.
There are two strategies how you can achieve that: Forwarding and Proxy.
Forwarding Strategy
The forwarding feature is the easier method for intercepting and recording responses from third-party services. However, it requires you to change the client’s base URL to direct requests to the mock server’s address.
When using the forwarding strategy, your client sends requests to an httpmock
mock server.
The mock server forwards requests that match the criteria defined in the
When structure to a predefined target base URL.
Let’s have a look at a basic forwarding example:
// Initialize the mock server for testinglet server = MockServer::start();
// Configure the server to forward all requests to the GitHub API,// instead of using mocked responses. The 'when' configuration allows// setting conditions for when forwarding should occur, using the same// structure familiar from creating mocks.server.forward_to("https://github.com", |rule| { rule.filter(|when| { when.any_request(); // Ensure all requests are forwarded. });});
You can use the forwarding functionality to record requests sent to the remote service.
Full Example
The following example demonstrates how you can use the forwarding feature to record requests sent to the GitHub API and the responses it returns.
#[cfg(all(feature = "proxy", feature = "record"))]#[test]fn record_github_api_with_forwarding_test() { // Let's create our mock server for the test let server = MockServer::start();
// We configure our server to forward the request to the target // host instead of answering with a mocked response. The 'when' // variable lets you configure rules under which forwarding // should take place. server.forward_to("https://api.github.com", |rule| { rule.filter(|when| { when.any_request(); // Ensure all requests are forwarded. }); });
let recording = server.record(|rule| { rule // Specify which headers to record. // Only the headers listed here will be captured and stored // as part of the recorded mock. This selective recording is // necessary because some headers may vary between requests // and could cause issues when replaying the mock later. // For instance, headers like 'Authorization' or 'Date' may // change with each request. .record_request_header("User-Agent") .filter(|when| { when.any_request(); // Ensure all requests are recorded. }); });
// Now let's send an HTTP request to the mock server. The request // will be forwarded to the GitHub API, as we configured before. let client = Client::new();
let response = client .get(server.url("/repos/torvalds/linux")) // GitHub requires us to send a user agent header .header("User-Agent", "httpmock-test") .send() .unwrap();
// Since the request was forwarded, we should see a GitHub API response. assert_eq!(response.status().as_u16(), 200); assert_eq!(true, response.text().unwrap().contains("\"private\":false"));
// Save the recording to // "target/httpmock/recordings/github-torvalds-scenario_<timestamp>.yaml". recording .save("github-torvalds-scenario") .expect("cannot store scenario on disk");}
Proxy Strategy
The proxy feature in httpmock
, while functional on its own, is particularly useful for recording
in scenarios where modifying or injecting the base URL used by the client is not possible.
Many SDKs, APIs, and HTTP clients support proxy server configuration. For example, the reqwest crate allows you to set up a proxy server with the following configuration:
// Create a client using the reqwest crate with a configured proxylet client = Client::builder() .proxy(reqwest::Proxy::all("my-proxy-server:8080").unwrap()) .build() .unwrap();
// Send a GET request and unwrap the resultlet response = client.get("https://github.com").send().unwrap();
In this example, each request is routed through the proxy server rather than directly to the requested domain host.
The proxy server then tunnels or forwards the request to the target host, which is github.com
in this case.
When configured as a proxy, httpmock
can intercept, record, and forward both requests and responses.
Full Example
#[cfg(all(feature = "proxy", feature = "record", feature = "experimental"))]#[test]fn record_with_proxy_test() { // Start a mock server to act as a proxy for the HTTP client let server = MockServer::start();
// Configure the mock server to proxy all incoming requests server.proxy(|rule| { rule.filter(|when| { when.any_request(); // Intercept all requests }); });
// Set up recording on the mock server to capture all proxied // requests and responses let recording = server.record(|rule| { rule.filter(|when| { when.any_request(); // Record all requests }); });
// Create an HTTP client configured to route requests // through the mock proxy server let github_client = Client::builder() // Set the proxy URL to the mock server's URL .proxy(reqwest::Proxy::all(server.base_url()).unwrap()) .build() .unwrap();
// Send a GET request using the client, which will be proxied by the mock server let response = github_client.get(server.base_url()).send().unwrap();
// Verify that the response matches the expected mock response assert_eq!(response.text().unwrap(), "This is a mock response");
// Save the recorded HTTP interactions to a file for future reference or testing recording .save("my_scenario_name") .expect("could not save the recording");}