Failure simulation

Production HTTP clients need more than happy-path JSON. Use these patterns to verify retries, backoff, timeouts, stream parsing, and fallback behavior.

Delayed response

Use delay when the server should accept the request but wait before sending the response.

1mokksy.get {
2  path("/slow")
3} respondsWith {
4  delay = 2.seconds
5  body = "eventually-ok"
6}
1mokksy.get(spec -> spec.path("/slow"))
2    .respondsWith(response -> response
3        .delayMillis(2_000)
4        .body("eventually-ok"));

Delayed chunks

Use delayBetweenChunks when the response should arrive incrementally and the client must process partial data.

1mokksy.get {
2  path("/slow-stream")
3} respondsWithStream {
4  delayBetweenChunks = 500.milliseconds
5  chunks += "first\n"
6  chunks += "second\n"
7}
1mokksy.get(spec -> spec.path("/slow-stream"))
2    .respondsWithStream(stream -> stream
3        .delayBetweenChunksMillis(500)
4        .chunk("first\n")
5        .chunk("second\n"));

Hanging request or stream

Use a stream that never completes when you need to verify client-side read timeouts, cancellation, or reconnect behavior.

1mokksy.get {
2  path("/never-finishes")
3} respondsWithSseStream {
4  flow = flow {
5    emit(ServerSentEvent(data = "started"))
6    awaitCancellation()
7  }
8}
1mokksy.get(spec -> spec.path("/never-finishes"))
2    .respondsWithSseStream(stream -> stream
3        .chunks(Stream.generate(() -> SseEvent.data("heartbeat")))
4        .delayBetweenChunksMillis(1_000));

Use this with a short client timeout to verify timeout handling.

Retry-after and rate limiting

Return 429 Too Many Requests with Retry-After when the client should back off and retry later.

1mokksy.post {
2  path("/payments")
3} respondsWith {
4  httpStatus = HttpStatusCode.TooManyRequests
5  headers += HttpHeaders.RetryAfter to "30"
6  body = """{"error":"rate_limited"}"""
7}
1mokksy.post(spec -> spec.path("/payments"))
2    .respondsWith(response -> response
3        .status(429)
4        .header("Retry-After", "30")
5        .body("{\"error\":\"rate_limited\"}"));

Malformed SSE

Send malformed event-stream data when you need to test parser failures and fallback behavior.

1mokksy.get {
2  path("/malformed-events")
3} respondsWithStream {
4  contentType = ContentType.Text.EventStream
5  chunks += "data: valid\n\n"
6  chunks += "this is not a valid event frame"
7}
1mokksy.get(spec -> spec.path("/malformed-events"))
2    .respondsWithStream(stream -> stream
3        .contentType("text/event-stream")
4        .chunk("data: valid\n\n")
5        .chunk("this is not a valid event frame"));

Partial failure

Send only part of the expected response when the client must handle incomplete transfers or timeout after partial data.

1mokksy.get {
2  path("/statement")
3} respondsWithStream {
4  chunks += "header\n"
5  chunks += "row-1\n"
6  delayBetweenChunks = 250.milliseconds
7}
1mokksy.get(spec -> spec.path("/statement"))
2    .respondsWithStream(stream -> stream
3        .chunk("header\n")
4        .chunk("row-1\n")
5        .delayBetweenChunksMillis(250));

Keep the client timeout lower than the full expected transfer time to verify partial-data handling.