Rig.TUnit.Microservices.Outbox
0.1.0-beta.2
dotnet add package Rig.TUnit.Microservices.Outbox --version 0.1.0-beta.2
NuGet\Install-Package Rig.TUnit.Microservices.Outbox -Version 0.1.0-beta.2
<PackageReference Include="Rig.TUnit.Microservices.Outbox" Version="0.1.0-beta.2" />
<PackageVersion Include="Rig.TUnit.Microservices.Outbox" Version="0.1.0-beta.2" />
<PackageReference Include="Rig.TUnit.Microservices.Outbox" />
paket add Rig.TUnit.Microservices.Outbox --version 0.1.0-beta.2
#r "nuget: Rig.TUnit.Microservices.Outbox, 0.1.0-beta.2"
#:package Rig.TUnit.Microservices.Outbox@0.1.0-beta.2
#addin nuget:?package=Rig.TUnit.Microservices.Outbox&version=0.1.0-beta.2&prerelease
#tool nuget:?package=Rig.TUnit.Microservices.Outbox&version=0.1.0-beta.2&prerelease
Rig.TUnit.Microservices.Outbox
Transactional-outbox testing helpers:
OutboxFixture,OutboxRelaySimulator,OutboxAssert,OutboxReplay, +CustomOutboxStore<TRow>for plug-your-own-schema.
What this package is
The command-side outbox-pattern testing kit. OutboxFixture owns an
IOutboxStore — either the default in-memory store (CAS-claim on read
for exactly-once under concurrency) or your own row type wrapped in
CustomOutboxStore<TRow>. OutboxRelaySimulator drains the pending
messages through your publish delegate; OutboxAssert verifies a
specific event type was enqueued and relayed; OutboxReplay simulates
a crashed-relay scenario to test idempotency.
Exactly-once semantics validated under 100 concurrent relay runs against the in-memory store.
When to use it
- Testing services that enqueue events inside the same transaction as the domain write.
- Verifying relay idempotency after crash-recovery.
- Asserting back-pressure / failure-handling under publish errors.
- Not for: full end-to-end broker tests — layer with the matching
Rig.TUnit.Messaging.*provider.
Prerequisites
- .NET 10 SDK
- If using
CustomOutboxStore<TRow>: your persistence layer (EF Core, Dapper, Marten, …).
Quick start
using Rig.TUnit.Microservices.Outbox;
await using var fx = new OutboxFixture();
await fx.InitializeAsync();
await fx.Store.EnqueueAsync(new OutboxMessage(
Guid.NewGuid(), "agg-1", "OrderPlaced", "{}", DateTimeOffset.UtcNow));
Options
| Property | Type | Default | Description |
|---|---|---|---|
TableName |
string |
"Outbox" |
Schema table name for SQL stores |
BatchSize |
int |
100 |
Rows per ReadPendingAsync claim |
ClaimTtl |
TimeSpan |
5m |
How long a claimed row stays hidden |
PublishRetries |
int |
3 |
Retries on publish failure before dead-letter |
Fixture + helper APIs
Rig.TUnit.Microservices.Outbox.Fixtures.OutboxFixtureRig.TUnit.Microservices.Outbox.Stores.IOutboxStoreRig.TUnit.Microservices.Outbox.Stores.CustomOutboxStore<TRow>Rig.TUnit.Microservices.Outbox.Helpers.OutboxRelaySimulatorRig.TUnit.Microservices.Outbox.Helpers.OutboxReplayRig.TUnit.Microservices.Outbox.Assertions.OutboxAssertRig.TUnit.Microservices.Outbox.Schema.OutboxSchema
Per-test isolation
OutboxFixture owns its store — in-memory is scoped per-fixture, and
CustomOutboxStore<TRow> inherits the isolation of the persistence
layer you plug in. IsolationKey can be used as the outbox table suffix
for parallel SQL-backed runs.
Parallelism + performance
- In-memory store:
EnqueueAsync~200 ns,ReadPendingAsync~1 µs. OutboxRelaySimulator.DrainAsyncis sequential by design — exactly- once guarantees require CAS-claim + single-writer-per-batch.- Parallelism across fixtures: safe; each owns its store.
Troubleshooting
- Duplicate publish — claim TTL expired before publish completed; a
second relay picked up the row. Raise
ClaimTtlor speed up the publish delegate. OutboxAssert.Contains<T>reports not-found — the message'sTypestring doesn't matchtypeof(T).FullName; either rename or overrideGetTypeName(Type).
See docs/troubleshooting.md#outbox.
Provider quirks + edge cases
OutboxSchemaships pre-built parameterised SQL for the common cases (BuildInsertSql,BuildReadPendingSql(N), etc). Non-standard providers (Cosmos, Mongo) need custom implementations ofIOutboxStore.- Relay simulator drains to exhaustion by default — large backlogs can
slow tests. Use
relay.DrainAsync(maxBatches: 10)for bounded runs.
Benchmarks
See OutboxBenchmarks.cs;
baseline in benchmarks/baseline-005.json. Exactly-once-under-100-
concurrent-relays is the marquee test.
Related docs
License
MIT. See LICENSE.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- Bogus (>= 35.6.1)
- Microsoft.Extensions.Configuration (>= 10.0.0)
- Microsoft.Extensions.Configuration.Binder (>= 10.0.0)
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 10.0.0)
- Microsoft.Extensions.Options (>= 10.0.0)
- Microsoft.Extensions.Options.DataAnnotations (>= 10.0.0)
- Rig.TUnit.Databases (>= 0.1.0-beta.2)
- Rig.TUnit.Messaging (>= 0.1.0-beta.2)
- TUnit.Core (>= 1.34.5)
NuGet packages (2)
Showing the top 2 NuGet packages that depend on Rig.TUnit.Microservices.Outbox:
| Package | Downloads |
|---|---|
|
Rig.TUnit.All
Meta-package containing every Rig.TUnit.* package. DISCOURAGED — prefer per-feature or per-stack meta-packages (Rig.TUnit, Rig.TUnit.Microservices). |
|
|
Rig.TUnit.Microservices
Meta-package: Core + Mediator + Grpc + Outbox + Tracing + Jwt + Seq — the opinionated microservice testing kit. |
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 0.1.0-beta.2 | 45 | 4/27/2026 |
| 0.0.0-alpha.0.14 | 52 | 4/26/2026 |