skip to content

In MockServer, what does error().withDropConnection(true) do to a cattle-auction bid client?

level: middleimportance: must knowfreq 68%

answer

  1. no status line reaches the client
  2. an action, not a canned response
  3. error() is the socket-level factory
  4. an IO failure, not a status code
  5. withDropConnection(true) writes zero bytes

basics

~20 s

The client gets no status line, no headers and no body. MockServer tears the socket down instead of replying, so the bid call fails as an IO error. That is a client branch no HTTP status code can reach.

solid answer

~50 s

`HttpError` is MockServer's socket-level action, produced by the static factory `error()`. Writing `.error(error().withDropConnection(true))` on an expectation for `POST /auctions/v1/lots/48/bids` means the server matches the request and then closes the connection without writing a single byte back. The bidding client therefore never parses a status line: it throws whatever its HTTP library raises for a connection that died in flight. `HttpError`'s other half is `withResponseBytes(byte[])`, which writes bytes you supply — valid HTTP or deliberate garbage — and then closes; the class also carries `withStreamError(long)`, `withStreamError(StreamErrorCode)` and `withStreamErrorCodeName(String)`. Use the drop form when you need the branch a stubbed 500 or 503 cannot reach: the timeout, retry and fallback code that only runs when there is no response object at all. Nothing about the request matching changes: MockServer selects the bid expectation exactly as usual, and only the answering step disappears.

code

java · 18 lines
java
import org.mockserver.integration.ClientAndServer;

import static org.mockserver.model.HttpError.error;
import static org.mockserver.model.HttpRequest.request;

ClientAndServer mockServer = ClientAndServer.startClientAndServer(1080);

// nothing is written; the socket is torn down
mockServer.when(request()
        .withMethod("POST")
        .withPath("/auctions/v1/lots/48/bids"))
    .error(error().withDropConnection(true));

// bytes are written, then the socket is closed
mockServer.when(request()
        .withMethod("POST")
        .withPath("/auctions/v1/lots/49/bids"))
    .error(error().withResponseBytes(new byte[] {0x41, 0x55, 0x43}));

go deeper

for a junior

Be ready to say that MockServer's error() action writes no reply at all. Name withDropConnection(true) as the switch and say the client sees a socket failure rather than a status code.

for a middle

Explain the ordering: MockServer matches the request first and then runs the action, so nothing is written back. Contrast the drop form with withResponseBytes, which writes bytes you choose and then closes.

for a senior

Show which cattle-auction client code a dropped connection actually reaches — the connect and read failure handlers, the retry guard, the fallback — and why a stubbed 503 leaves all of that untested.

for a principal

Own where socket-level failure injection belongs in a test estate: which suites carry it, how many such expectations a service needs before the signal stops improving, and who keeps them honest.

## The action that writes nothing MockServer expectations end in exactly one action, and `error(HttpError)` is the one that operates below HTTP. `HttpError` is produced by the static factory `error()`, and `withDropConnection(true)` on it means precisely what it says: when a request matches, the server does not write a status line, does not write headers, does not write a body, and closes the connection. There is no "empty response" involved and no code of any kind. The matching still happens — the expectation's request matcher selects `POST /auctions/v1/lots/48/bids` as usual — and only the answering step is replaced. ## What the bid client actually observes From the client's side the sequence is: - the connection opens and the bid request is written; - the server accepts and matches it; - no response bytes ever arrive; - the socket closes, and the client's HTTP library raises an IO or connection-reset failure. The practical consequence is that no `response` variable is ever assigned. Code shaped like `if (response.status() == 503) { retry(); }` is unreachable in this scenario, because control left through an exception long before that line. What runs instead is the catch block, the retry policy that is keyed on transport exceptions, and any fallback the bidding client has for "the ring service is unreachable". ## HttpError's full surface The class is deliberately small, and knowing all of it stops you inventing methods: - `error()` — the static factory - `withDropConnection(Boolean)` — write nothing at all, then close - `withResponseBytes(byte[])` — write exactly these bytes, then close - `withStreamError(long)` - `withStreamError(StreamErrorCode)` - `withStreamErrorCodeName(String)` Note what is *not* there: no status code, no header setter, no body builder. Those belong to `HttpResponse`, which is a different action. ## Drop versus bytes-then-close The two halves of `HttpError` fail a client in different places, and choosing wrongly means testing the wrong layer. | MockServer call | bytes on the wire | which client layer fails | |---|---|---| | `error().withDropConnection(true)` | none | the socket and IO layer | | `error().withResponseBytes(bytes)` | exactly what you supply | the HTTP parser, or the body decoder | | `respond(response().withStatusCode(503))` | a valid reply | the status-handling branch | So if the thing under test is a JSON decoder that must survive a truncated bid confirmation, `withResponseBytes` is the right tool. If the thing under test is a retry policy that only fires on transport exceptions, it is `withDropConnection`. ## Why no status code can substitute This is the whole point of the surface, and it is worth being able to say crisply. Every status code — 500, 502, 503, 504 — arrives inside a syntactically valid HTTP message. The client parses it successfully and hands your code a response object. A dropped connection never gets that far. Two branches therefore exist in almost every HTTP client that a test suite must cover separately: 1. the response arrived and its code was bad; 2. no response arrived at all. A suite that only ever stubs error codes has covered the first and left the second entirely untested, and the second is the one that tends to hang, retry forever, or swallow an exception silently. ## Getting it right in a cattle-auction suite A few habits keep this clean. Give the broken-connection expectation its own path or its own lot number so it is obvious in the stub set which endpoint is deliberately hostile. Assert on the exception type your client raises rather than on a message string, because the message comes from the HTTP library and is not part of any contract. And name the class in the code review: `HttpError.withDropConnection(true)` reads unambiguously, whereas a bare `withDropConnection(true)` invites the reader to assume it hangs off `ConnectionOptions`, which it does not. For orientation across products, WireMock expresses the same idea as a decoration on its reply builder rather than as a distinct action — `aResponse().withFault(Fault.CONNECTION_RESET_BY_PEER)` — which is why snippets do not port between the two servers by search and replace. A short checklist for the review: - name the class, not only the method, whenever you write about it; - keep the deliberately hostile expectation on its own path or lot number; - assert on the exception type your client raises, never on a library's message text; - do not pair a broken-connection expectation with a status-code assertion, because there is no status to assert on. Each of those is cheap, and together they stop the next reader assuming that a bid endpoint is broken by accident rather than on purpose.

  • In MockServer, what does HttpError.withResponseBytes(byte[]) do that withDropConnection(true) does not?
    It writes the exact bytes you hand it and then closes, so the client reads something rather than nothing. That lets you feed a cattle-auction client a truncated body, a half-written header block, or bytes that are not HTTP at all, and watch its parser fail rather than its socket layer. `withDropConnection(true)` writes nothing, so the parser is never reached.
  • In MockServer, which client behaviour does a dropped connection reach that a stubbed 503 cannot?
    The transport-failure path. A 503 arrives as a well-formed reply, so the client's status handling runs on a real response object. `error().withDropConnection(true)` writes nothing, so the client instead raises a socket or IO exception and takes its connect-and-read failure branch. Any retry or fallback code guarded by that exception type is unreachable from a status code alone.

A stubbed 503 is the auctioneer shouting "lot withdrawn" — an answer you can act on. A dropped connection is the phone line going dead mid-bid: nothing to read, only silence to time out on.

saying these in an interview costs you the question

  • Says the client receives an empty 200 response
  • Thinks a stubbed 500 exercises the same client branch
  • Expects MockServer to write a status code anyway
  • Calls withDropConnection a delay rather than a socket teardown
  • Believes a dropped connection still returns headers