# httpmock: mocking HTTP services in Rust with a diff on failure

> httpmock is a Rust library for standing up mock HTTP servers inside your tests, and its distinguishing feature is the failure message, which shows a diff between the expected and the actual request rather than a bare assertion. It runs in process, as a standalone container, and as a proxy that records and replays third party services.

**httpmock/httpmock** — HTTP mocking library for Rust

- Repository: https://github.com/httpmock/httpmock
- Website: https://httpmock.rs
- Stars: 675 · Forks: 61
- Language: Rust
- License: MIT
- Published: 2026-10-21 · Updated: 2026-10-21 · Language: en
- Canonical page: https://hysenlabs.com/projects/httpmock-httpmock

## A mock server in six lines of Rust

httpmock is a Rust library for mocking responses from HTTP services, and the getting started example is short enough to read in one go.

The dependency goes in the dev-dependencies section:

```toml
[dev-dependencies]
httpmock = "0.9.0"
```

Then the test itself starts a lightweight mock server, creates a mock on it, sends a request and asserts both that the mock was called and that the response was what was expected:

```rust
use httpmock::prelude::*;

// Start a lightweight mock server.
let server = MockServer::start();

// Create a mock on the server.
let mock = server.mock(|when, then| {
    when.method(GET)
        .path("/translate")
        .query_param("word", "hello");
    then.status(200)
        .header("content-type", "text/html; charset=UTF-8")
        .body("hola");
});

// Send an HTTP request to the mock server. This simulates your code.
let response = isahc::get(server.url("/translate?word=hello")).unwrap();

// Ensure the specified mock was called exactly one time (or fail with a
// detailed error description).
mock.assert();

// Ensure the mock server did respond as specified.
assert_eq!(response.status(), 200);
```

The API shape is the point. The when and then closures read like a sentence about the request, with the matcher chain on one side and the response on the other, and the prelude import brings the whole vocabulary in with one line. The README calls it a simple, expressive, fluent API, and that is an accurate description of what the closure style buys you.

The server is started with no arguments, so it picks a free port, and the URL is derived from the server handle rather than hard coded. That is what makes the library usable in a test suite that runs in parallel, which the feature list names as a supported property.

## The failure message is the feature to judge it by

When the expectations do not match, mock.assert() fails with a detailed error description that includes a diff between the expected and the actual HTTP request. The example in the readme is the kind of output most mocking libraries do not produce.

```bash
0 of 1 expected requests matched the mock specification.
Here is a comparison with the most similar unmatched request (request number 1):

------------------------------------------------------------
1 : Query Parameter Mismatch
------------------------------------------------------------
Expected:
    key    [equals]  word
    value  [equals]  hello-rustaceans

Received (most similar query parameter):
    word=hello

All received query parameter values:
    1. word=hello

Matcher:  query_param
Docs:     https://docs.rs/httpmock/0.9.0/httpmock/struct.When.html#method.query_param
```

Several things are happening there that are worth naming. It reports how many of the expected requests matched, which tells you whether the request never arrived or arrived and was wrong. It picks the most similar unmatched request rather than dumping all of them. It then names the specific mismatch as a query parameter mismatch, shows the expected key and value, shows what was actually received, and lists all the received values for that parameter so a typo in one of several values is visible.

Finally it points at the matcher that failed and links to the documentation for that specific method, with the version in the URL. When a test fails at two in the morning, that link is worth more than another paragraph of prose.

The dependency list explains how the diff is produced. assert-json-diff handles JSON body comparison, similar provides the text diffing, and colored is an optional dependency that presumably tints the output. The advanced verification and debugging support is called out in the feature list with a link to a detailed post about creating mocks.

## Matchers, recording, proxying and fault injection

Beyond the basic when and then closures, the feature list covers a range of things you would otherwise assemble yourself.

On the matching side there are many built-in helpers, with regex, JSON, serde, cookies and more named, plus custom request matchers for the cases the built-ins do not cover. The matcher vocabulary is what decides whether a test is readable, and having a JSON matcher that knows about structure rather than byte equality is the difference between a useful test and a brittle one.

There is a record and playback mode for third party services, which is the feature that lets you test code against an API you do not control. There is a forward and proxy mode, so traffic can be forwarded to a real service with some of it mocked, which is how you test a partial integration without a full stub of the whole API. There is HTTPS support, and fault and network delay simulation, which matters because a client that has never seen a timeout is not a client you want to release.

There is a standalone mode with an accompanying Docker image, and a file based mock specification using YAML files, so a set of mocks can live in a file that a colleague or a CI pipeline can read. The reference docs mention advanced verification and debugging support with diff generation between actual and expected request values.

The core is fully asynchronous, with both synchronous and asynchronous APIs offered, and parallel test execution is supported. The combination of a fully asynchronous core and a synchronous API is the pragmatic choice for a test library, since most Rust test code is synchronous and forcing every caller into an async context would be a poor trade.

## The standalone server, its environment variables and its container

The standalone mode is the piece that turns httpmock from a test library into a service other things can point at, and it is configured entirely through environment variables.

The Dockerfile is a two stage build. The builder stage starts from rust:1.88, copies the manifest, the source and the certs directory, and runs cargo install with all features enabled. The final stage is a debian slim image with ca-certificates installed, and it copies the single built binary across.

The environment variables in that image describe the whole surface. HTTPMOCK_PORT sets the TCP port the mock server listens on, and the image default is 5050. HTTPMOCK_MOCK_FILES_DIR points at the directory containing file based mock specs in YAML, with the comment noting the typo in the original source, and HTTPMOCK_DISABLE_ACCESS_LOG is described in a comment as being considered true or disabled if the variable exists at all, even when its value is empty. HTTPMOCK_REQUEST_HISTORY_LIMIT is set to 100, and RUST_LOG is set to httpmock=info. The entrypoint is the httpmock binary with the --expose flag.

The compose file shows the intended usage. The service builds from the local context, publishes port 5050, mounts a YAML mock file from the tests resources directory into the container, and sets HTTPMOCK_MOCK_FILES_DIR to that mount point. The file it mounts is named simple_static_mock.yaml, which suggests the file based specification supports simple static mocks as a starting point.

The certificates are worth noting. There is a certs/ directory at the repository root, and the Makefile has a certs target that regenerates a self signed certificate authority with openssl, creating a ca.key and a ca.pem valid for 36525 days with the common name httpmock. HTTPS support in a test library needs a certificate the client will accept, and generating one locally is the only way to do that without shipping a real one.

## A strict lint policy and a feature matrix test

The engineering discipline in this repository is visible in its configuration files, and it is stricter than most Rust projects.

The manifest sets the warnings group to deny at the level of the package, with a comment explaining that the allowed lints are a temporary allow list which should be removed over time. Individual lints are set to warn, and unsafe_code is forbidden outright. The clippy configuration does the same thing, with the all group denied and a specific redundant_clone denied as well. The priority setting of minus one on each group is the detail that makes this correct, since it keeps a group below the individual entries so a specific lint level always wins over the group.

The project also denies the unused extern crates and redundant imports lints, and sets the minimum Rust version to 1.88.0 with the 2024 edition.

The testing setup is more unusual. The Makefile has a test-matrix target that runs cargo hack with the feature powerset at depth 2, which tests every feature alone and every pair of features, using the in-process simulated standalone server. The comment says it mirrors the CI feature-matrix workflow, and that cargo-hack skips combinations which are equivalent under feature implications. There is a separate test-full target that runs the full feature power set against a real standalone server container, with the simulated server disabled by an environment variable.

That is a thorough way to test a library whose features are numerous and interacting. A feature-gated crate tested only with all features enabled hides the combinations that break, and a library that offers a standalone mode, a proxy mode, YAML files, HTTPS, fault injection and several optional dependencies has a lot of combinations. The rest of the tooling follows the same pattern: cargo fmt for formatting, cargo clippy, cargo audit, cargo deny check for licence and advisory policy, and cargo tarpaulin for coverage with several configurations, including one that runs the tests with a Rust backtrace and trace logging enabled.

## Conclusion

httpmock suits a Rust team whose tests talk to HTTP and want a mock that runs in process without a container, especially one that values readable failure output and parallel test execution. It does not suit a project that needs to mock protocols other than HTTP, and you should check the version you actually pull, because the readme and the manifest ask for 0.9.0 while the newest tag is v0.8.3. Before adopting it, run the feature matrix locally, decide between in process and standalone mode, and confirm the certificate story for HTTPS mocks before a CI job depends on it.

## FAQ

### What is httpmock used for?

It is an HTTP mocking library for Rust that stands up mock servers in tests, with a fluent API, built-in request matchers for regex, JSON, serde and cookies, custom matchers, file based YAML mock specifications, HTTPS support, fault and network delay simulation, and record and playback of third party services.

### How do I add httpmock to a Rust project?

Add it to the dev-dependencies section of Cargo.toml as httpmock = "0.9.0", then use MockServer::start() to spin up a server, create a mock with the when and then closures, and call mock.assert() to verify the expected requests were received.

### What does httpmock print when a test fails?

A detailed error description including a diff between the expected and the actual request, showing how many expected requests matched, the most similar unmatched request, the specific mismatch such as a query parameter mismatch, the expected and received values, all received values for that parameter, and a link to the documentation of the matcher that failed.

### Can httpmock run as a standalone service?

Yes, there is a standalone mode with a Docker image, configured through environment variables including HTTPMOCK_PORT, which defaults to 5050 in the image, HTTPMOCK_MOCK_FILES_DIR for the directory of YAML mock specifications, HTTPMOCK_DISABLE_ACCESS_LOG and HTTPMOCK_REQUEST_HISTORY_LIMIT.

## Sources

- [httpmock/httpmock on GitHub](https://github.com/httpmock/httpmock)
- [License: MIT](https://github.com/httpmock/httpmock/blob/master/LICENSE)
- [Project website](https://httpmock.rs)
- [README](https://github.com/httpmock/httpmock/blob/master/README.md)
- [Releases](https://github.com/httpmock/httpmock/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/httpmock-httpmock
