Skip to content

v7: Obvs.Testing - in-memory transport and test harness - #109

Merged
christopherread merged 3 commits into
v7from
v7-testing
Oct 25, 2026
Merged

christopherread merged 3 commits into
v7from
v7-testing

Conversation

@christopherread

@christopherread christopherread commented Oct 25, 2026 •

Copy link
Copy Markdown
Owner

Second v7 increment, part of #88 (design, section 5). Targets v7.

API

// The in-memory transport, as a service's transport
obvs.AddService<IOrderMessage>("orders", service => service.UseInMemory());              // one broker per container
obvs.AddService<IOrderMessage>("orders", service => service.UseInMemory(sharedBroker));  // connects several hosts

// The harness
await using var harness = await ObvsTestHarness.StartAsync(
    services => services.AddObvs(obvs => obvs.AddService<IOrderMessage>("orders", s => s.UseInMemory())),
    options => { options.DefaultTimeout = ...; options.TimeProvider = fakeTime; options.Scheduler = testScheduler; });

await harness.Client.SendAsync(new PlaceOrder("o-1"));
var placed = await harness.Published<OrderPlaced>(e => e.OrderId == "o-1");   // also Sent, Requested, Replied

Behaviour

In-memory transport (UseInMemory):

  • Destinations: one per service and kind of message, named like the broker transports name theirs, so messages never cross between services.
  • Request/response: responses are matched to requests the same way as on real transports.
  • Delivery: asynchronous and in order, on a scheduler. The task pool is the default; a TestScheduler holds messages until it runs.
  • Brokers: one per container, or share a broker between hosts to connect them.

Harness (ObvsTestHarness):

  • Watches the broker itself, so it sees every message, including commands sent by an app that's only a client.
  • Returns immediately: Sent, Published, Requested and Replied return as soon as a matching message passes through, including one that already has.
  • Clear timeouts: a timeout throws ObvsTestTimeoutException listing every message that did pass through.
  • Virtual time: timeouts run on options.TimeProvider, so a FakeTimeProvider tests them without real waiting.

Other changes:

  • ObvsBuilder and ObvsServiceBuilder expose Services (the IServiceCollection) for transports that register their own services.
  • CI packs Obvs.Testing, but only on master and release tags, never from v7.

The scheduler and virtual-time support covers what #37 (@fernando-almeida) asked for.

Tests (written first)

  • 19 new tests in Obvs.Testing.Tests: 8 for the transport and 11 for the harness, agreed before implementation. All failed with NotImplementedException first.
  • Repeat runs: all 19 passed in 10 consecutive runs.
  • Whole solution: a clean build has 0 warnings and 0 errors, and all 162 unit tests pass.
  • Package: Obvs.Testing builds and packs for net472, netstandard2.0 and net10.0.

Limitations

  • Messages are passed by reference, not serialized, so serialization bugs aren't caught. An optional serialize-and-deserialize mode can follow once v7 serializers are configured per service.
  • Routing between services is tested strictly. ShouldOnlyDeliverMessagesToTheirOwnService collects every command the bus receives on a test scheduler and expects exactly one. It fails if a command reaches both services, which I confirmed by temporarily breaking destination naming. An earlier version filtered by message type and would have missed that, so it was tightened in a second commit.

🤖 Generated with Claude Code

UseInMemory() carries a service's messages on an in-memory broker, one per
container unless a broker is shared between hosts. Delivery behaves like a
real transport: per-service destinations, request/response correlation,
and asynchronous, in-order delivery on a scheduler (the task pool by
default, or a TestScheduler).

ObvsTestHarness starts a host on its own broker and records every message
passing through it. Sent, Published, Requested and Replied return as soon
as a matching message arrives, including one that already has, and time
out with ObvsTestTimeoutException listing the messages seen. Timeouts run
on a configurable TimeProvider, so they can be tested with virtual time.

ObvsBuilder and ObvsServiceBuilder now expose the IServiceCollection, for
transports that register their own services.

The scheduler and virtual-time support covers the testing needs raised in
#37 by Fernando Almeida.

Part of #88.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@christopherread christopherread added this to the v7 milestone Oct 25, 2026
The test filtered with OfType<IOrderMessage>, so it passed even if a
command reached both services. It now collects every command the bus
receives on a test scheduler and expects exactly one; it fails when
destinations ignore the service name.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Brings in the NetMQ receive-loop fix (#110, via #111) so the NetMQ broker
tests pass on CI.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@christopherread
christopherread merged commit ffa79d9 into v7 Oct 25, 2026
1 check passed
@christopherread
christopherread deleted the v7-testing branch October 25, 2026 17:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant