Skip to content

Component Diagrams

Aryeh Citron edited this page Apr 5, 2026 · 16 revisions

Component Diagrams (C4-style)

TestTrackingDiagrams can aggregate all tracked interactions across your entire test suite to auto-generate a C4-style component diagram. This diagram shows every discovered participant (services, event brokers, databases) and their relationships — giving you a high-level architecture overview derived directly from real test traffic.

This feature is enabled by default (GenerateComponentDiagram = true).


Table of Contents


Quick Start

Add GenerateComponentDiagram = true to your ReportConfigurationOptions (this is the default):

var options = new ReportConfigurationOptions
{
    GenerateComponentDiagram = true  // already the default
};

That's it. When your test run completes and reports are generated, a ComponentDiagram.puml and ComponentDiagram.html file will appear in the Reports/ directory alongside your existing report files.


How It Works

RequestResponseLogger.RequestAndResponseLogs  (all tests, already in memory)
        │
        ▼
ComponentDiagramGenerator.ExtractRelationships()
        │  Filters: ignores responses, override markers, action markers, TrackingIgnore
        │  Groups by: (CallerName, ServiceName, Protocol)
        │  Counts: total calls, distinct test IDs, distinct HTTP methods
        ▼
ComponentDiagramGenerator.GeneratePlantUml()
        │  Classifies participants as Person() or System()
        │  Generates C4 Component PlantUML syntax
        ▼
ComponentDiagramReportGenerator
        │  Writes ComponentDiagram.puml  (raw PlantUML)
        │  Writes ComponentDiagram.html  (HTML wrapper)
        ▼
Reports/ directory
  1. Extract — All RequestResponseLog entries accumulated during the test run are filtered to only include meaningful request logs (excluding responses, override markers, action start markers, and ignored logs). They are then grouped by the unique combination of (CallerName, ServiceName, Protocol) to discover relationships.

  2. Aggregate — For each relationship, the generator counts total calls, distinct test IDs (how many tests exercised this path), and the distinct HTTP methods or event types used.

  3. Generate — The aggregated relationships are converted into PlantUML C4 Component diagram syntax. Participants are classified as Person() (pure callers, typically your test client) or System() (everything else).

  4. Write — The PlantUML source is written to a .puml file and wrapped in an HTML page for easy viewing.


Configuration

Enabling Component Diagrams

GenerateComponentDiagram defaults to true. To disable:

var options = new ReportConfigurationOptions
{
    GenerateComponentDiagram = false  // opt out
};

To customise the diagram:

var options = new ReportConfigurationOptions
{
    ComponentDiagramOptions = new ComponentDiagramOptions
    {
        Title = "My Service Architecture"
    }
};

When GenerateComponentDiagram is false, no component diagram files are generated and there is zero overhead.

ComponentDiagramOptions

Property Type Default Description
FileName string "ComponentDiagram" Base filename for the generated .puml and .html files
EmbedInFeaturesReport bool true Reserved for future use — will embed the diagram in the FeaturesReport.html
Title string "Component Diagram" The title displayed in the PlantUML diagram and HTML page
PlantUmlTheme string? null Optional PlantUML theme name (e.g. "cerulean", "superhero")
ParticipantFilter Func<string, bool>? null Predicate to include/exclude participants by name
RelationshipLabelFormatter Func<ComponentRelationship, string>? null Custom function to format relationship labels
ShowRelationshipFlows bool false When true, adds a clickable relationship list below the component diagram. Each relationship opens a popup showing the aggregated internal flow (OTel spans) for that caller→service path. Requires InternalFlowTracking = true.
RelationshipFlowStyle InternalFlowDiagramStyle ActivityDiagram Visual style for relationship flow popups. ActivityDiagram shows a PlantUML diagram. CallTree shows an HTML nested list.
ShowSystemFlameChart bool false When true, adds a system-level flow section below the component diagram showing all test spans in a sequential flame chart with a Gantt toggle.

Output Files

When enabled, two files are generated in the Reports/ directory:

File Description
ComponentDiagram.puml Raw PlantUML C4 source. This is a plain text file that can be version-controlled and diffed to detect architectural changes between commits.
ComponentDiagram.html A standalone HTML page containing the PlantUML source in a collapsible <details> element. Can be opened directly in a browser.

The filenames can be customised via ComponentDiagramOptions.FileName.


Participant Classification

Participants are automatically classified based on their role in the tracked interactions:

Classification PlantUML element Rule
Person Person(alias, "Name") The participant only appears as a CallerName and never as a ServiceName in any tracked log. This is typically your test client / SUT caller.
System System(alias, "Name") The participant appears as a ServiceName in at least one tracked log. This includes your SUT, its downstream dependencies, event brokers, and databases.

A service that both receives calls and makes downstream calls (e.g. your SUT calling a payment service) is rendered as a System because it appears as a ServiceName.


Relationship Labels

By default, each relationship label contains:

<Protocol>: <Methods> — <CallCount> calls across <TestCount> tests

Examples:

  • HTTP: GET, POST — 14 calls across 8 tests
  • Publish: Publish — 3 calls across 2 tests
  • CosmosDB: Query, Upsert — 5 calls across 3 tests

The protocol is determined automatically:

  • HTTP — For standard HTTP requests (MetaType == Default). The distinct HTTP method names (GET, POST, PUT, etc.) are listed.
  • Event/Custom — For non-HTTP interactions (MetaType == Event), the protocol is the string value of the Method property on RequestResponseLog (e.g. "Publish", "CosmosDB", "SQL").

Customisation

Custom Title

var options = new ComponentDiagramOptions
{
    Title = "Order Service Architecture"
};

PlantUML Theme

Apply any built-in PlantUML theme:

var options = new ComponentDiagramOptions
{
    PlantUmlTheme = "cerulean"
};

This inserts !theme cerulean into the generated PlantUML.

Participant Filter

Exclude specific participants from the component diagram:

var options = new ComponentDiagramOptions
{
    // Exclude internal helper services and health check endpoints
    ParticipantFilter = name => name != "InternalHelper" && name != "HealthCheck"
};

The filter is a Func<string, bool> that receives the participant name. Return true to include, false to exclude. When a participant is excluded, all relationships involving that participant (as either caller or service) are removed.

Custom Relationship Labels

Override the default label format with a custom formatter:

var options = new ComponentDiagramOptions
{
    RelationshipLabelFormatter = rel =>
        $"{rel.Protocol} ({rel.CallCount} calls)"
};

The ComponentRelationship record passed to the formatter contains:

Property Type Description
Caller string The calling participant name
Service string The receiving participant name
Protocol string "HTTP" or the event/custom protocol name
Methods HashSet<string> Distinct method names (e.g. GET, POST, Publish)
CallCount int Total number of calls across all tests
TestCount int Number of distinct tests that exercised this relationship

Example Output

Given a test suite that exercises an Order Service with downstream dependencies:

Generated PlantUML (ComponentDiagram.puml)

@startuml
!include <C4/C4_Component>

title Order Service Architecture

Person(webApp, "WebApp")
System(orderService, "OrderService")
System(paymentService, "PaymentService")
System(kafka, "Kafka")
System(cosmosDb, "CosmosDB")

Rel(webApp, orderService, "HTTP: GET, POST — 14 calls across 8 tests")
Rel(orderService, paymentService, "HTTP: POST — 6 calls across 4 tests")
Rel(orderService, kafka, "Publish: Publish — 3 calls across 2 tests")
Rel(orderService, cosmosDb, "CosmosDB: Query, Upsert — 5 calls across 3 tests")

@enduml

This diagram is derived entirely from actual test HTTP traffic and event tracking — it cannot drift from reality as long as your tests pass.


Relationship Flows

When ShowRelationshipFlows = true and InternalFlowTracking is enabled, the component diagram HTML page includes:

  1. A clickable relationship list — Below the component diagram image, each discovered relationship (e.g. "WebApp → OrderService") is listed as a clickable item.
  2. Flow popups — Clicking a relationship opens a modal popup showing:
    • An aggregated flow diagram (activity diagram or call tree) combining all spans from all tests that exercised that relationship.
    • A summary table showing the top 20 tests sorted by duration, with span count and total duration.
var options = new ReportConfigurationOptions
{
    GenerateComponentDiagram = true,
    ComponentDiagramOptions = new ComponentDiagramOptions
    {
        ShowRelationshipFlows = true,
        RelationshipFlowStyle = InternalFlowDiagramStyle.ActivityDiagram
    }
};

For test suites with many tests (1500+), the summary table shows the top 20 tests by duration with a "and N more tests" indicator at the bottom.


System Flow

When ShowSystemFlameChart = true and InternalFlowTracking is enabled, the component diagram page includes an inline System Flow section:

  • Flame Chart — A sequential test flame chart where each test occupies its own horizontal band with a label divider. Spans are rendered as horizontal bars proportional to their duration within each test's time window.
  • Gantt — A PlantUML Gantt chart showing all spans from all tests.

A toggle button lets you switch between the Flame Chart and Gantt views.

var options = new ReportConfigurationOptions
{
    GenerateComponentDiagram = true,
    ComponentDiagramOptions = new ComponentDiagramOptions
    {
        ShowSystemFlameChart = true
    }
};

Integration with PlantUML IKVM

The component diagram feature works with the standard PlantUML server rendering. If you're using PlantUML IKVM for local rendering, the .puml file can still be rendered locally using the IKVM renderer, but the component diagram report itself currently outputs raw PlantUML source in the HTML file rather than a rendered image.

Future versions may add support for rendering the component diagram as an image via the local renderer.


Performance

The component diagram feature has negligible performance impact:

Aspect Impact
Data source Reads the same RequestResponseLogger.RequestAndResponseLogs that are already in memory — no additional data collection
Processing Single O(n) pass over log entries with GroupBy — microseconds for typical test suites (100–10,000 logs)
Memory Aggregated relationships are O(unique participant pairs), typically < 50 entries
File I/O Two small files (.puml + .html) written in parallel with existing report generation
When disabled Zero cost — a single if guard at the entry point

Limitations & Known Behaviours

  1. Request-only aggregation — Only request logs are counted. Response logs are excluded to avoid double-counting (a request and its response represent a single interaction).

  2. Protocol detection — HTTP interactions (MetaType == Default) are grouped under the "HTTP" protocol. Event/message interactions (MetaType == Event) use the Method value as the protocol name. If multiple event types share the same method name but different service names, they are tracked as separate relationships.

  3. Override and action markersIsOverrideStart, IsOverrideEnd, and IsActionStart log entries are excluded from aggregation, consistent with how they're handled in sequence diagrams.

  4. Static data — The component diagram is generated from RequestResponseLogger.RequestAndResponseLogs, which is a static concurrent queue. In test runs where the queue is shared across frameworks or assemblies, all interactions are included.

  5. No rendered image — The current implementation outputs raw PlantUML source. You can paste it into plantuml.com or use a PlantUML VS Code extension to render it visually.


API Reference

ComponentDiagramGenerator (static class)

// Extract unique relationships from tracked logs
public static ComponentRelationship[] ExtractRelationships(
    IEnumerable<RequestResponseLog> logs,
    Func<string, bool>? participantFilter = null);

// Generate C4 PlantUML from relationships
public static string GeneratePlantUml(
    ComponentRelationship[] relationships,
    ComponentDiagramOptions? options = null);

ComponentDiagramReportGenerator (static class)

// Generate the .puml and .html report files
public static ComponentDiagramResult GenerateComponentDiagramReport(
    IEnumerable<RequestResponseLog> logs,
    ComponentDiagramOptions? options = null);

ComponentDiagramResult (record)

public record ComponentDiagramResult(
    string PumlFilePath,    // Absolute path to the generated .puml file
    string HtmlFilePath,    // Absolute path to the generated .html file
    string PlantUml);       // The raw PlantUML string

ComponentRelationship (record)

public record ComponentRelationship(
    string Caller,              // Calling participant name
    string Service,             // Receiving participant name
    string Protocol,            // "HTTP" or custom protocol name
    HashSet<string> Methods,    // Distinct method names
    int CallCount,              // Total calls across all tests
    int TestCount);             // Distinct test count

ComponentDiagramOptions (record)

public record ComponentDiagramOptions
{
    public string FileName { get; set; } = "ComponentDiagram";
    public bool EmbedInFeaturesReport { get; set; } = true;
    public string Title { get; set; } = "Component Diagram";
    public string? PlantUmlTheme { get; set; }
    public Func<string, bool>? ParticipantFilter { get; set; }
    public Func<ComponentRelationship, string>? RelationshipLabelFormatter { get; set; }
    public bool ShowRelationshipFlows { get; set; }
    public InternalFlowDiagramStyle RelationshipFlowStyle { get; set; } = InternalFlowDiagramStyle.ActivityDiagram;
    public bool ShowSystemFlameChart { get; set; }
}

ReportConfigurationOptions (new properties)

public bool GenerateComponentDiagram { get; set; } = true;           // Default: true
public ComponentDiagramOptions? ComponentDiagramOptions { get; set; }  // Default: null

Home


Demo


Getting Started

Common Tasks

Integration Guides

Extensions

Configuration

Features

Reference

Clone this wiki locally