Skip to content

mock://

Producer-only test sink. Every exchange routed to a mock:// endpoint is recorded; the test then introspects it via Endpoint methods (ReceivedCount, Received, ExpectedBodiesReceived, AssertIsSatisfied, …). Supports a full test-sink surface (expectedCount, assertPeriod, retainFirst/retainLast, failFast, log, resultWaitTime, copyOnExchange, sleepForEmptyTest, reportGroup) and adds Helix-specific extensions for request-matched stubs and canned replies.

Use this for unit/integration testing routes. Do not use it in production routes — it is a sink that holds exchanges in memory.

URI shape

mock://<name>[?expectedCount=5&assertPeriod=200ms&retainLast=10&...]

<name> is a free-form identifier. Endpoints with the same name share the recorder; re-creation returns the existing endpoint.

Parameters

Param Type Default Description
name path — Endpoint identifier.
expectedCount integer -1 Producer: expected number of messages. -1 = unset. 0 triggers the sleepForEmptyTest confirmation path.
assertPeriod duration 0 Producer: grace period after expectedCount is satisfied. Re-checks to ensure no MORE messages arrived.
copyOnExchange boolean true Producer: deep-copy the incoming exchange before recording (prevents downstream mutation from changing what the assert sees).
failFast boolean true Producer: fail at the first detected mismatch (vs wait for the full count and report everything).
log boolean false Producer: log every incoming message at INFO.
reportGroup integer 0 Producer: log a throughput report every N messages (0 = off).
resultMinimumWaitTime duration 0 Producer: minimum wait before AssertIsSatisfied can return — lets in-flight tail work arrive.
resultWaitTime duration 5s Producer: maximum wait for AssertIsSatisfied(0).
retainFirst integer -1 Producer: keep only the first N exchanges (memory savings). TotalReceivedCount still tracks the real arrival count.
retainLast integer -1 Producer: keep only the last N exchanges.
sleepForEmptyTest duration 0 Producer: when expectedCount=0, sleep this long to confirm no messages arrived.

Durations accept Go format or integer milliseconds.

Assertion API (Go)

The URI params above tune the asserter; the actual assertion calls live on *mock.Endpoint. Pull the endpoint from the running context (ctx.ResolveEndpoint(...).(*mock.Endpoint)) and call any combination of:

  • ExpectedMessageCount(n) — exact count.
  • SetMinimumExpectedMessageCount(n) — at least N.
  • ExpectedBodiesReceived(b1, b2, …) — bodies in this order.
  • ExpectedBodiesReceivedInAnyOrder(b1, b2, …) — order-insensitive.
  • ExpectedHeaderReceived(name, value) — every exchange has header.
  • ExpectedHeaderValuesReceivedInAnyOrder(name, v1, v2, …) — multi-value match.
  • Expects(func(i, ex) error) — arbitrary per-exchange predicate.
  • ExpectsAscending(expr) / ExpectsDescending(expr) / ExpectsNoDuplicates(expr).
  • WhenAnyExchangeReceived(processor) — run on every arrival.
  • ReturnReplyBody(any) / ReturnReplyHeader(name, value) — canned reply for InOut callers.
  • AddStub(&mock.Stub{...}) — request-matched canned response (Helix extension).
  • AssertIsSatisfied(timeout) — wait & validate.

Stubs (Helix extension)

mock.Stub registers a request-matched canned response. Multiple stubs are tried in registration order; the first whose matchers all hit wins. Fields:

type Stub struct {
    // Match — any combination; all set fields must match.
    BodyContains   string
    BodyEquals     string
    HeaderEquals   map[string]string
    HeaderContains map[string]string

    // Respond.
    Body    any
    Headers map[string]any
    Status  int             // sets HelixHttpResponseCode (useful for http stubs)
    Delay   time.Duration   // simulated latency
    Error   string          // if non-empty, the stub returns an error
}

Body shape

Producer-only. Consumers are not supported.

Examples

Assert a route sent two messages with specific bodies:

- id: pipeline
  from: direct://input
  steps:
    - to: mock://output?expectedCount=2&resultWaitTime=2s
ep := ctx.MustResolveEndpoint("mock://output").(*mock.Endpoint)
ep.ExpectedBodiesReceived("alice", "bob")
// drive the route…
if err := ep.AssertIsSatisfied(0); err != nil {
    t.Fatal(err)
}

Cap memory on a long-running soak:

- to: mock://events?retainFirst=50&retainLast=50&log=false

(only first 50 + last 50 are kept; TotalReceivedCount() still reports the real number.)

Request-matched stub for an InOut caller:

ep.AddStub(&mock.Stub{
    BodyContains: "ping",
    Body:         []byte(`{"pong":true}`),
    Headers:      map[string]any{"Content-Type": "application/json"},
    Status:       200,
})

Behavior notes

  • retainFirst + retainLast together keep the head and tail of the stream with a hole in the middle — useful for "first 10, last 10 out of 1M" forensics. TotalReceivedCount() always reports the true arrival count.
  • assertPeriod is a tail-check. It runs after the expected count is met; if more messages arrive in that window, the assert fails.
  • copyOnExchange=true by default so a downstream step that mutates headers doesn't change what was recorded.
  • InOut callers. ReturnReplyBody / ReturnReplyHeader set the exchange Out so the caller of a mock:// sees a reply. Stubs do the same with Stub.Body/Headers/Status.

Source

  • pkg/component/mock/mock.go — Component, Endpoint, Producer, Consumer (always errors), Stub.
  • Tests: pkg/component/mock/mock_test.go, pkg/component/mock/mock_parity_test.go.

See also

  • For mocking outbound HTTP without a real server, see also the in-process http-server:// running on a test port.