Repository navigation
Integration ServiceBus Extension
The Kronikol.Extensions.ServiceBus package adds Azure Service Bus messaging operation tracking to your test diagrams. Instead of opaque AMQP traffic, your sequence diagrams show classified operations like Send → orders-queue or Receive ← notifications-topic.
Using a shared library or abstraction layer? If your code doesn't use the
ServiceBusClientSDK directly — e.g. it goes through MassTransit, NServiceBus, a shared messaging library, or a custom abstraction — see the MassTransit Extension or Tracking Custom Dependencies for alternative approaches includingMessageTrackerandTrackingProxy<T>.
The Azure Service Bus SDK (Azure.Messaging.ServiceBus) uses AMQP by default, which does not go through an HttpMessageHandler pipeline. This extension uses a wrapper/decorator pattern instead — TrackingServiceBusClient wraps the real ServiceBusClient and returns tracked senders (TrackingServiceBusSender) and receivers (TrackingServiceBusReceiver) that intercept every operation and log it to RequestResponseLogger.
Because it logs to the same RequestResponseLogger as the standard TestTrackingMessageHandler, Service Bus operations appear alongside your HTTP API calls in the same sequence diagram — showing the complete flow from test → API → Service Bus.
Messaging operations (Send, Receive, Schedule, Peek) are tagged with MetaType.Event, so their notes are drawn on the event card.
dotnet add package Kronikol.Extensions.ServiceBusThe package asks for Azure.Messaging.ServiceBus 7.11.0 or later, and Microsoft.Extensions.DependencyInjection.Abstractions
10.0.0 or later, so a suite can pin whichever 7.x from 7.11.0 its application ships with. The package's own suite runs
on the newest 7.x, and in CI on 7.11.0 as well. A project that takes the client only through this package resolves
7.11.0; reference the client yourself for a newer one. The floor is 7.11.0 because that release added the
DeadLetterMessageAsync overload with properties and a reason, which the tracking receiver overrides so that it
reaches the receiver it wraps. The DI abstractions' floor is 10.0.0, the first release of the major, where the
suite passes; before 5.27.10 it was 10.0.7, which the Kronikol core's Microsoft.AspNetCore.Mvc.Testing asked for.
ServiceBusClient.GetMessageSessionsAsync, new in 7.21.0, is past the floor, so TrackingServiceBusClient cannot
override it, and through the tracking client it throws NullReferenceException. Call it on Inner, the client the
tracking client wraps; the listing is not drawn.
The extension supports three verbosity levels that control how much detail appears in the diagrams:
| Level | Label shown | URI shown | Message body |
|---|---|---|---|
| Raw | Enum name (Send, SendBatch) |
servicebus://{queue} |
Included |
| Detailed | Operation with arrows (Send → orders-queue, Receive ← orders-queue) |
servicebus://{queue} or servicebus://{topic}/{subscription}
|
Included |
| Summarised | Simple name (Send, Receive, Complete) |
servicebus://{queue}/ |
None |
The default is Detailed.
| Operation | Raw | Detailed | Summarised |
|---|---|---|---|
| Send a message | Send |
Send → orders-queue |
Send |
| Send a batch (5 messages) | SendBatch |
Send (×5) → orders-queue |
Send |
| Schedule a message | Schedule |
Schedule → orders-queue |
Schedule |
| Receive a message | Receive |
Receive ← orders-queue |
Receive |
| Receive a batch (10 messages) | ReceiveBatch |
Receive (×10) ← orders-queue |
Receive |
| Peek | Peek |
Peek ← orders-queue |
Peek |
| Complete | Complete |
Complete |
Complete |
| Abandon | Abandon |
Abandon |
Abandon |
| Dead-letter | DeadLetter |
DeadLetter |
DeadLetter |
| Defer | Defer |
Defer |
Defer |
| Renew lock | RenewMessageLock |
RenewLock |
RenewLock |
The classifier recognises these Service Bus operations:
| Operation | Method Name |
|---|---|
Send |
SendMessageAsync |
SendBatch |
SendMessagesAsync |
Schedule |
ScheduleMessageAsync, ScheduleMessagesAsync
|
CancelSchedule |
CancelScheduledMessageAsync, CancelScheduledMessagesAsync
|
Receive |
ReceiveMessageAsync |
ReceiveBatch |
ReceiveMessagesAsync |
Peek |
PeekMessageAsync, PeekMessagesAsync
|
Complete |
CompleteMessageAsync |
Abandon |
AbandonMessageAsync |
DeadLetter |
DeadLetterMessageAsync |
Defer |
DeferMessageAsync |
RenewMessageLock |
RenewMessageLockAsync |
RenewSessionLock |
RenewSessionLockAsync |
GetSessionState |
GetSessionStateAsync |
SetSessionState |
SetSessionStateAsync |
StartProcessing |
StartProcessingAsync |
StopProcessing |
StopProcessingAsync |
v2.27.14+ Use the built-in DI extension method. It automatically resolves
IHttpContextAccessorfrom DI and handles the dual-resolution test identity pattern.
// In your test's ConfigureTestServices:
services.AddServiceBusTestTracking(options =>
{
options.ServiceName = "ServiceBus";
options.CallerName = "OrdersApi";
options.Verbosity = ServiceBusTrackingVerbosity.Detailed;
options.CurrentTestInfoFetcher = CurrentTestInfo.Fetcher;
});This uses DecorateAll<ServiceBusClient> internally — it finds all existing ServiceBusClient registrations, replaces each with a TrackingServiceBusClient (which inherits from ServiceBusClient), and preserves the original service lifetime. No-op if no ServiceBusClient registrations exist. No changes to production code required.
v2.27.14 Breaking Change:
TrackingServiceBusClientnow inherits fromServiceBusClient(previously a composition wrapper). TheInnerproperty and existing API remain available, but the service is now registered asServiceBusClient(not asTrackingServiceBusClient). Code that resolvesServiceBusClientfrom DI will receive the tracking subclass transparently.
Create a TrackingServiceBusClient that wraps your existing ServiceBusClient:
var trackingOptions = new ServiceBusTrackingOptions
{
ServiceName = "ServiceBus",
CallerName = "My API",
Verbosity = ServiceBusTrackingVerbosity.Detailed,
CurrentTestInfoFetcher = CurrentTestInfo.Fetcher
};
var realClient = new ServiceBusClient("your-connection-string");
var trackedClient = new TrackingServiceBusClient(realClient, trackingOptions);
// Use trackedClient instead of realClient
var sender = trackedClient.CreateSender("orders-queue");
await sender.SendMessageAsync(new ServiceBusMessage("hello"));
var receiver = trackedClient.CreateReceiver("orders-queue");
var message = await receiver.ReceiveMessageAsync();Use AddServiceBusTestTracking() with a pre-constructed options object:
// In your test WebApplicationFactory or DI setup:
services.AddServiceBusTestTracking(new ServiceBusTrackingOptions
{
ServiceName = "ServiceBus",
CallerName = "OrdersApi",
Verbosity = ServiceBusTrackingVerbosity.Detailed,
CurrentTestInfoFetcher = CurrentTestInfo.Fetcher
});This overload is preserved for backward compatibility and uses the same DecorateAll<ServiceBusClient> pattern internally.
| Property | Type | Default | Description |
|---|---|---|---|
ServiceName |
string |
"ServiceBus" |
Display name in diagrams for the Service Bus |
CallerName |
string |
"Caller" |
Calling service name in diagrams |
Verbosity |
ServiceBusTrackingVerbosity |
Detailed |
Verbosity level (Raw, Detailed, Summarised) |
CurrentTestInfoFetcher |
Func<(string Name, string Id)>? |
null |
Required: provides test context for log correlation |
CurrentStepTypeFetcher |
Func<string?>? |
null |
Has no effect: nothing in the Service Bus package reads it (only the HTTP handler's option of the same name does). Its removal is a v6 question |
HttpContextAccessor |
IHttpContextAccessor? |
null |
Optional — enables dual-resolution of test identity from HTTP headers. Auto-resolved by DI extensions (v2.26.3+). See [[HTTP Tracking Setup › Dual-Resolution Test Identity (v2.23.0+) |
SetupVerbosity |
ServiceBusTrackingVerbosity? |
null |
Verbosity override for the Setup phase. See Phase-Aware Tracking |
ActionVerbosity |
ServiceBusTrackingVerbosity? |
null |
Verbosity override for the Action phase. See Phase-Aware Tracking |
TrackDuringSetup |
bool |
true |
When false, tracking is suppressed during Setup. See Phase-Aware Tracking
|
TrackDuringAction |
bool |
true |
When false, tracking is suppressed during Action. See Phase-Aware Tracking
|
PropagateTestIdentity |
bool |
true |
When true, senders inject kronikol-test-name / kronikol-test-id into ApplicationProperties and receivers extract them to establish TestIdentityScope. See [[Background Thread Correlation › Solution 1b: Automatic Message Header Propagation (v2.34.0+) |
AutoCorrelateOnConsume |
bool |
true |
When true, received messages auto-populate TestCorrelationStore for parallel-safe background thread correlation |
v2.23.0+ Dual-Resolution:
ServiceBusTrackeraccepts an optionalIHttpContextAccessor? httpContextAccessorconstructor parameter for resolving test identity from HTTP request headers when running inside the SUT's request pipeline. v2.26.3+: SetHttpContextAccessoronServiceBusTrackingOptionsinstead — the tracker reads it automatically. DI extensions likeAddServiceBusTestTracking()auto-resolve it from the service provider. See HTTP Tracking Setup › Dual-Resolution Test Identity (v2.23.0+) for details.
Because ServiceBusSender and ServiceBusReceiver are sealed classes in the Azure SDK, this extension uses wrapper classes rather than DelegatingHandler or DispatchProxy:
ServiceBusClient (real)
└── TrackingServiceBusClient (subclass, extends ServiceBusClient)
├── CreateSender() → TrackingServiceBusSender (wraps ServiceBusSender)
└── CreateReceiver() → TrackingServiceBusReceiver (wraps ServiceBusReceiver)
Each wrapper delegates all calls to the real instance and adds tracking before/after each operation. The Inner property on each wrapper provides escape-hatch access to the original SDK object.
TrackingServiceBusClient hands out the processors, session processors and rule managers of the client it wraps,
untracked; disposing it, or a tracking sender or receiver, disposes the one it wraps.
TrackingServiceBusSender tracks:
-
SendMessageAsync: logs the message body (at Raw and Detailed) -
SendMessagesAsync: logs the count, for a list of messages and, from 5.27.3, for aServiceBusMessageBatch. A batch's messages carry no test identity, as the caller built the batch before the sender saw it, so a receiver in another service cannot tell which test sent them; send a list where that matters -
ScheduleMessageAsync: logs the message body; the response includes the sequence number -
CancelScheduledMessageAsync: logs the sequence number
Untracked (pass-through): CreateMessageBatchAsync, CloseAsync, DisposeAsync
TrackingServiceBusReceiver tracks:
-
ReceiveMessageAsync— logs received message body in response -
ReceiveMessagesAsync— logs count in response -
PeekMessageAsync— logs peeked message body in response CompleteMessageAsyncAbandonMessageAsync-
DeadLetterMessageAsync: every overload, with a reason, with properties, or both (the last two from 5.27.3); logs the dead-letter reason (at Raw and Detailed) DeferMessageAsyncRenewMessageLockAsync
Untracked (pass-through): CloseAsync, DisposeAsync
ServiceBusTracker implements ITrackingComponent and auto-registers with TrackingComponentRegistry on construction. If the tracker is registered but never processes any operations, a passive warning appears in the console output and (when DiagnosticMode=true) in the HTML diagnostic report. This never throws or fails tests — see Diagnostics and Debugging for details.
When your application processes Service Bus messages on background threads (e.g. inside a ServiceBusProcessor or hosted service), the test framework's TestContext is unavailable. The extension solves this automatically via ApplicationProperties propagation.
-
Sender side:
TrackingServiceBusSenderinjectskronikol-test-nameandkronikol-test-idinto each message'sApplicationPropertiesbefore sending. -
Receiver side:
TrackingServiceBusReceiverreads these properties from each received message and callsTestIdentityScope.SetFromMessage(). - All subsequent tracking within the message handler resolves to the originating test.
var options = new ServiceBusTrackingOptions
{
PropagateTestIdentity = false,
// ...
};See Background Thread Correlation › Solution 1b: Automatic Message Header Propagation (v2.34.0+) for the full architecture overview.
A send or a receive that throws is answered ! and the exception's type, with its message chain in error, and keeps the message as its note, none at Summarised (5.34.3, #232); before, it had no status, as a success has none. History's older rules still read the call as the release before recorded it, so a suite that upgrades reads no change there (Cross-Run History).
Home · 🔍 Search this wiki Full-text search across every page. The “Pages” list above matches page and heading names, not page content.
Getting Started
- Quick Start (xUnit)
- Project Templates
- AI Integration Prompt
- Framework Integration Guides
- Migrating to v5
- Migrating to v4
Common Tasks
Integration Guides
- Integration xUnit3
- Integration xUnit2
- Integration NUnit
- Integration MSTest
- Integration TUnit
- Integration BDDfy xUnit3
- Integration LightBDD xUnit2
- Integration LightBDD xUnit3
- Integration LightBDD TUnit
- Integration ReqNRoll xUnit2
- Integration ReqNRoll xUnit3
- Integration ReqNRoll TUnit
- Integration Playwright
Uninstrumentable / polyglot backends
- Integration ProxyTap Extension
- Integration TcpTap Extension
- Integration Otlp Extension
- Exporting to OpenTelemetry
- Ingesting External Captures
- Ingesting from Jest
- Integration Cucumber Messages
- Capture-Time Redaction
Extensions
- Integration AtlasDataApi Extension
- Integration BigQuery Extension
- Integration Bigtable Extension
- Integration BlobStorage Extension
- Integration ClickHouse Extension
- Integration CloudStorage Extension
- Integration CosmosDB Extension
- Integration Dapper Extension
- Integration DynamoDB Extension
- Integration EF Core Relational Extension
- Integration Elasticsearch Extension
- Integration EventBridge Extension
- Integration EventHubs Extension
- Integration Grpc Extension
- Integration Kafka Extension
- Integration MassTransit Extension
- Integration MongoDB Extension
- Integration MySqlConnector Extension
- Integration Npgsql Extension
- Integration Oracle Extension
- Integration PubSub Extension
- Integration Redis Extension
- Integration S3 Extension
- Integration ServiceBus Extension
- Integration SNS Extension
- Integration Spanner Extension
- Integration SqlClient Extension
- Integration Sqlite Extension
- Integration SQS Extension
- Integration StorageQueues Extension
- Integration OpenTelemetry Extension
- Integration DispatchProxy Extension
- Integration MediatR Extension
- Integration PlantUML IKVM
Configuration
- Tracking Dependencies
- Tracking Custom Dependencies
- HTTP Tracking Setup
- Report Configuration
- Diagram Customisation
- Phase-Aware Tracking
- Content Formatting
- PlantUML Server Configuration
Features
- Generated Reports
- Report Themes
- Search and Filter Syntax
- Component Diagrams
- PlantUML Browser Rendering
- Inline SVG Rendering
- Internal Flow Tracking
- Tags and Attributes
- Excluding Requests
- Excluded Headers
- Multi-Host Test Architectures
- Event-Driven Architecture Testing
- Service Bus Tracking Patterns
- Background Thread Correlation
- Parallel-Safe Background Correlation
- Event & Message Tracking
- Assertion Tracking
- Step Tracking
- Tabular Attributes
- Large Response and Diagram Handling
- Querying Reports
- Service Card
- Cross-Run History
- Diagnostics and Debugging
- CI Summary Integration
- CI Artifact Upload
- Merging Parallel Reports
Reference