MockRestServiceServer can intercept a RestClient builder and verify requests without a network connection; it cannot prove transport failures.
Spring RestClient mock server: assert outgoing request shape before socket tests
Intercept at the builder
A receipt gateway sends a POST with an idempotency key and JSON payload. Bind MockRestServiceServer to the same RestClient.Builder used to create the gateway client, register the expected method, path and header, then return a controlled response. This quickly catches a misplaced header or wrong URI. The mock request factory must be installed before build() creates the client; binding a different builder tests nothing. A loopback socket test covers real HTTP framing and connection behavior.
Separate response mapping from transport
Use mock responses for success, conflict and error JSON, and assert the gateway converts each into the right domain outcome. The mock server does not simulate DNS lookup, TLS, socket timeouts or connection pooling in the same way as the deployed client. A real mock web server can cover those paths with the actual HTTP stack. Layered failure tests keep fast request-shape checks without overstating their evidence.
Verify every expectation
Call mockServer.verify() after the gateway action so an expected request that never happened fails the test. Use distinct idempotency values and exact target paths, not a broad match that could accept the wrong customer. Keep secrets out of assertion failure text. If the application mutates a shared RestClient.Builder, create a fresh builder per test to avoid expectations leaking into another case.
Implementation contract
RestClient.Builder receiptBuilder = RestClient.builder().baseUrl(serviceBaseAddress);
MockRestServiceServer mockServer = MockRestServiceServer.bindTo(receiptBuilder).build();
RestClient receiptClient = receiptBuilder.build();
mockServer.expect(requestTo(serviceBaseAddress + "/receipts/r-47"))
.andExpect(method(HttpMethod.GET))
.andRespond(withSuccess("{\"status\":\"issued\"}", MediaType.APPLICATION_JSON));
ReceiptSnapshot snapshot = gatewayUsing(receiptClient).load("r-47");
mockServer.verify();Cost and verification
No socket means fast, deterministic request matching. The tradeoff is missing transport behavior; keep a smaller live HTTP test suite for timeouts, TLS and connection reuse.
Common Mistakes
- Do not bind the mock server to a builder different from the production client under test.
- Do not omit verify() and assume every expectation ran.
- Do not call this a test of the real network transport.
Read next
Test Spring RestClient on a socket without claiming a remote service, Spring test failures: prove the layer that can reject the request, Spring MockMvc tests: HTTP behavior without claiming a real network test, Spring Boot config tests: what a child JVM proves and what deployment still owes, Spring Security 401 versus 403: authentication and access are separate failures.
