Skip to content

Integration PubSub Extension

Aryeh Citron edited this page Apr 27, 2026 · 13 revisions

Integration: Google Cloud Pub/Sub Extension

Track Google Cloud Pub/Sub operations in your test diagrams using the TestTrackingDiagrams.Extensions.PubSub NuGet package. This extension wraps PublisherClient and SubscriberClient to capture publish/subscribe operations.

Using a shared library or abstraction layer? If your code doesn't use the Pub/Sub SDK directly — e.g. it goes through a shared messaging library, wrapper, or custom abstraction — this extension won't be able to intercept the underlying calls. See Tracking Custom Dependencies for alternative approaches including MessageTracker and TrackingProxy<T>.

Installation

dotnet add package TestTrackingDiagrams.Extensions.PubSub

Quick Start

Publisher

using TestTrackingDiagrams.Extensions.PubSub;

var publisher = await PublisherClient.CreateAsync(topicName);
var trackingPublisher = new TrackingPublisherClient(publisher, new PubSubTrackingOptions
{
    ServiceName = "PubSub",
    CallingServiceName = "OrdersApi",
    CurrentTestInfoFetcher = () => (TestContext.CurrentTestName, TestContext.CurrentTestId)
});

await trackingPublisher.PublishAsync("Hello, Pub/Sub!");

Subscriber

var subscriber = await SubscriberClient.CreateAsync(subscriptionName);
var trackingSubscriber = new TrackingSubscriberClient(subscriber, new PubSubTrackingOptions
{
    ServiceName = "PubSub",
    CallingServiceName = "WorkerService",
    CurrentTestInfoFetcher = () => (TestContext.CurrentTestName, TestContext.CurrentTestId)
});

await trackingSubscriber.StartAsync(async (msg, ct) =>
{
    // Process message
    return SubscriberClient.Reply.Ack;
});

How It Works

Google Cloud Pub/Sub uses gRPC internally, so DelegatingHandler interception is not viable. Instead, this extension uses a wrapper/decorator pattern:

  • TrackingPublisherClient wraps PublisherClient and logs publish operations
  • TrackingSubscriberClient wraps SubscriberClient and intercepts the message handler callback

PubSubTracker

Both wrappers use PubSubTracker as a central logging helper. It implements ITrackingComponent and handles request/response logging with RequestResponseMetaType.Event for messaging semantics.

Supported Operations

Operation Enum Value Source
Publish (single) PubSubOperation.Publish TrackingPublisherClient
Publish (batch) PubSubOperation.PublishBatch TrackingPublisherClient
Receive PubSubOperation.Receive TrackingSubscriberClient callback
Pull PubSubOperation.Pull Low-level API
Acknowledge PubSubOperation.Acknowledge Low-level API
ModifyAckDeadline PubSubOperation.ModifyAckDeadline Low-level API
StartSubscriber PubSubOperation.StartSubscriber Lifecycle
StopSubscriber PubSubOperation.StopSubscriber Lifecycle

Resource Name Extraction

GCP Pub/Sub uses full resource paths like projects/my-project/topics/my-topic. At Detailed verbosity, short names are extracted:

  • projects/my-project/topics/my-topicmy-topic
  • projects/my-project/subscriptions/my-submy-sub

Verbosity Levels

PubSubTrackingVerbosity.Summarised

  • Label: Operation name (e.g. Publish)
  • Content: Omitted
  • URI: pubsub:///my-topic

PubSubTrackingVerbosity.Detailed

  • Label: Operation with target (e.g. Publish → my-topic, Receive ← my-sub)
  • Content: Message data (UTF-8 text)
  • URI: pubsub:///my-topic

PubSubTrackingVerbosity.Raw

  • Label: Full info including resource paths
  • Content: Message data + response details
  • URI: pubsub:///projects/p/topics/my-topic

Configuration

DI Registration (v2.23.11+)

Recommended. Registers a singleton PubSubTracker in DI with IHttpContextAccessor automatically resolved. Consumers can then inject PubSubTracker when constructing wrapper clients.

// In your test's ConfigureTestServices:
services.AddPubSubTestTracking(options =>
{
    options.ServiceName = "PubSub";
    options.CallingServiceName = "OrdersApi";
    options.CurrentTestInfoFetcher = () => (TestContext.Current!.Test.TestDisplayName, TestContext.Current.Test.UniqueID);
});

Then use the tracker when creating wrapper clients:

// Resolve from DI:
var tracker = sp.GetRequiredService<PubSubTracker>();
var publisher = await PublisherClient.CreateAsync(topicName);
var trackingPublisher = new TrackingPublisherClient(publisher, new PubSubTrackingOptions
{
    ServiceName = "PubSub",
    CallingServiceName = "OrdersApi",
    CurrentTestInfoFetcher = /* same as above */
});

Manual Configuration

new PubSubTrackingOptions
{
    ServiceName = "PubSub",
    CallingServiceName = "OrdersApi",
    Verbosity = PubSubTrackingVerbosity.Detailed,
    CurrentTestInfoFetcher = () => (testName, testId)
}

PubSubTrackingOptions

Property Type Default Description
ServiceName string "PubSub" Display name in diagrams for the Pub/Sub service
CallingServiceName string "Caller" Calling service name in diagrams
Verbosity PubSubTrackingVerbosity Detailed Verbosity level (Raw, Detailed, Summarised)
CurrentTestInfoFetcher Func<(string Name, string Id)>? null Required: provides test context for log correlation
CurrentStepTypeFetcher Func<string?>? null Optional — returns the current BDD step type (Given/When/Then)

v2.23.0+ Dual-Resolution: PubSubTracker accepts an optional IHttpContextAccessor? httpContextAccessor constructor parameter for resolving test identity from HTTP request headers when running inside the SUT's request pipeline. See HTTP Tracking Setup#Dual-Resolution Test Identity (v2.23.0+) for details. The AddPubSubTestTracking() DI helper (v2.23.11+) resolves this automatically. | SetupVerbosity | PubSubTrackingVerbosity? | null | Verbosity override for the Setup phase. See Phase-Aware Tracking | | ActionVerbosity | PubSubTrackingVerbosity? | 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 |

ITrackingComponent

PubSubTracker implements ITrackingComponent and auto-registers:

  • ComponentName: "PubSubTracker ({ServiceName})"
  • WasInvoked: true after first operation
  • InvocationCount: Total operations logged

See Also

Home


Demo


Getting Started

Common Tasks

Integration Guides

Extensions

Configuration

Features

Reference

Clone this wiki locally