Test Double Options for Command Processor
How-to · Applies to Brighter V10
Test Double Overview
When handlers depend on IAmACommandProcessor to publish events or send commands, you need a way to verify those interactions in tests. The Paramore.Brighter.Testing package provides SpyCommandProcessor - a test double that records all calls for later verification.
Installation
Add a reference to the Paramore.Brighter.Testing package in your test project:
<PackageReference Include="Paramore.Brighter.Testing" />Or reference the project directly:
<ProjectReference Include="..\..\src\Paramore.Brighter.Testing\Paramore.Brighter.Testing.csproj" />Using SpyCommandProcessor
Basic Usage
Inject SpyCommandProcessor as your IAmACommandProcessor dependency and verify interactions after exercising the handler:
// Arrange
var spy = new SpyCommandProcessor();
var handler = new PlaceOrderHandler(spy);
var command = new PlaceOrder { ProductId = "WIDGET-1", Quantity = 3 };
// Act
handler.Handle(command);
// Assert - verify the handler published an OrderPlaced event
spy.WasCalled(CommandType.Publish).ShouldBeTrue();
var published = spy.Observe<OrderPlaced>();
published.ProductId.ShouldBe("WIDGET-1");API Layers
SpyCommandProcessor provides a layered API, from simple checks to detailed inspection:
Layer 1: Quick Checks
For the most common verification needs:
Observe<T>() dequeues the next request of type T from the queue. This is useful for verifying multiple calls in sequence. It throws InvalidOperationException if no matching request is found.
Layer 2: Request Inspection
For examining all calls without consuming them:
Layer 3: Full Details
For advanced scenarios requiring complete call information:
CommandType Values
Each IAmACommandProcessor method maps to a CommandType:
Send
Send
SendAsync
SendAsync
Publish
Publish
PublishAsync
PublishAsync
Post
Post
PostAsync
PostAsync
DepositPost
Deposit
DepositPostAsync
DepositAsync
ClearOutbox
Clear
ClearOutboxAsync
ClearAsync
Call
Call
Scheduled (sync)
Scheduler
Scheduled (async)
SchedulerAsync
Verifying Send/Publish/Post Calls
Verifying Send
Verifying Publish
Verifying Post (External Bus)
Verifying the Outbox Pattern (DepositPost + ClearOutbox)
When handlers use the outbox pattern, SpyCommandProcessor tracks deposits separately:
State Management
Use Reset() between test scenarios when reusing a spy:
Extending SpyCommandProcessor
All methods on SpyCommandProcessor are virtual, allowing you to create specialized subclasses:
This is useful for testing error handling paths in your handlers.
Alternative: Using Mocking Frameworks
If you prefer mocking frameworks, you can mock IAmACommandProcessor directly. SpyCommandProcessor is a convenience for when you want a lightweight, framework-independent test double.
Moq
NSubstitute
Test Double Best Practices
Prefer
SpyCommandProcessorover mocking frameworks for simple verification - it requires no additional dependencies and provides a clearer API.Use
Observe<T>()for sequential verification when order matters. UseGetRequests<T>()when you just need all requests of a type.Use
Reset()if sharing a spy across multiple test methods in a fixture rather than creating new instances. Creating a new instance per test class constructor is preferred.Test behaviors, not interactions - verify that the right events/commands were produced with the right data, rather than asserting on exact call sequences.
Extend with subclasses when you need the spy to throw exceptions or return specific values from
Call<T, TResponse>().
Last updated
Was this helpful?
