-
Notifications
You must be signed in to change notification settings - Fork 1
Integration MSTest
Local integration guide: A copy of this guide is also available at
docs/integration-mstest.md.
This guide walks you through integrating Kronikol with MSTest. After completing this guide, your MSTest tests will automatically generate:
- PlantUML sequence diagrams from HTTP traffic between your service and its dependencies
- HTML reports with embedded diagrams
- YAML specification files
- .NET 10.0 SDK or later
- An ASP.NET Core API project to test (your "Service Under Test")
- Basic familiarity with MSTest
Create a new MSTest test project:
dotnet new mstest -n MyApi.Tests.Componentdotnet add package Kronikol.MSTest
dotnet add package Microsoft.AspNetCore.Mvc.Testing
dotnet add package Microsoft.NET.Test.Sdk
dotnet add package MSTestMSTest uses [AssemblyInitialize] and [AssemblyCleanup] for global setup and teardown.
using Kronikol;
using Kronikol.MSTest;
namespace MyApi.Tests.Component.Infrastructure;
[TestClass]
public class TestRun : DiagrammedTestRun
{
[AssemblyInitialize]
public static void AssemblyInitialize(TestContext context)
{
Setup();
// Optional: start any HTTP fakes here
}
[AssemblyCleanup]
public static void AssemblyCleanup()
{
EndRunTime = DateTime.UtcNow;
MSTestReportGenerator.CreateStandardReportsWithDiagrams(
TestContexts,
StartRunTime,
EndRunTime,
new ReportConfigurationOptions
{
SpecificationsTitle = "My API Specifications"
});
// Optional: dispose HTTP fakes here
}
}Critical points:
-
[AssemblyInitialize]and[AssemblyCleanup]must bestaticmethods in a class marked with[TestClass]. - Call
Setup()in[AssemblyInitialize]— this records theStartRunTime. - Report generation happens in
[AssemblyCleanup]after all tests have run.
using Microsoft.AspNetCore.Mvc.Testing;
using Microsoft.AspNetCore.TestHost;
using Kronikol.MSTest;
namespace MyApi.Tests.Component.Infrastructure;
public abstract class BaseFixture : DiagrammedComponentTest, IDisposable
{
private static readonly WebApplicationFactory<Program>? SFactory;
protected HttpClient Client { get; }
private const string ServiceUnderTestName = "My API";
static BaseFixture()
{
SFactory = new WebApplicationFactory<Program>().WithWebHostBuilder(builder =>
{
builder.ConfigureTestServices(services =>
{
services.TrackDependenciesForDiagrams(new MSTestTestTrackingMessageHandlerOptions
{
CallerName = ServiceUnderTestName,
PortsToServiceNames =
{
{ 80, ServiceUnderTestName },
{ 5001, "Downstream Service A" }
}
});
});
});
}
protected BaseFixture()
{
Client = SFactory!.CreateTestTrackingClient(
new MSTestTestTrackingMessageHandlerOptions
{
FixedNameForReceivingService = ServiceUnderTestName
});
}
public void Dispose() => Client.Dispose();
}Key points:
-
DiagrammedComponentTestprovides[TestInitialize](sets the async-local test context) and[TestCleanup](enqueues test metadata for report collection). -
MSTestTestTrackingMessageHandlerOptionsuses the async-localTestContextto resolve the current test's identity.
Tests are written as regular MSTest [TestMethod] methods. Use the [Endpoint] and [HappyPath] attributes to add metadata for the report.
using Kronikol.MSTest;
namespace MyApi.Tests.Component.Scenarios;
[Endpoint("/cake")]
public partial class Cake_Feature
{
[TestMethod]
[HappyPath]
public async Task Calling_Create_Cake_Endpoint_Returns_Cake()
{
await Given_a_valid_post_request_for_the_Cake_endpoint();
await When_the_request_is_sent_to_the_cake_post_endpoint();
await Then_the_response_should_be_successful();
}
[TestMethod]
public async Task Calling_Create_Cake_Endpoint_Without_Eggs_Returns_Bad_Request()
{
await Given_a_valid_post_request_for_the_Cake_endpoint();
await But_the_request_body_is_missing_eggs();
await When_the_request_is_sent_to_the_cake_post_endpoint();
await Then_the_response_http_status_should_be_bad_request();
}
}using System.Net;
using System.Net.Http.Json;
using MyApi.Tests.Component.Infrastructure;
namespace MyApi.Tests.Component.Scenarios;
[TestClass]
public partial class Cake_Feature : BaseFixture
{
private HttpResponseMessage? _response;
private async Task Given_a_valid_post_request_for_the_Cake_endpoint()
{
// Build your request using Client
}
private async Task But_the_request_body_is_missing_eggs()
{
// Modify request
}
private async Task When_the_request_is_sent_to_the_cake_post_endpoint()
{
_response = await Client.PostAsJsonAsync("cake", /* request */);
}
private async Task Then_the_response_should_be_successful()
{
_response!.StatusCode.Should().Be(HttpStatusCode.OK);
}
private async Task Then_the_response_http_status_should_be_bad_request()
{
_response!.StatusCode.Should().Be(HttpStatusCode.BadRequest);
}
}dotnet testAfter the tests complete, check the bin/Debug/net10.0/Reports/ folder:
| File | Description |
|---|---|
Specifications.html |
HTML specifications with embedded PlantUML sequence diagrams |
TestRunReport.html |
HTML test run report with diagrams and execution summary |
Specifications.yml |
YAML specifications |
You can customise diagrams within a test using TrackingDiagramOverride:
using Kronikol.MSTest;
// Insert a delimiter between multiple requests in the diagram
TrackingDiagramOverride.InsertTestDelimiter("Step 1");
// Insert raw PlantUML markup
TrackingDiagramOverride.InsertPlantUml("note over MyApi : Custom note");
// Override the start/end of diagram generation
TrackingDiagramOverride.StartOverride();
TrackingDiagramOverride.EndOverride();
// Explicitly mark the beginning of the setup phase (produces a visual separator in the diagram)
TrackingDiagramOverride.StartSetup();
// Explicitly mark the boundary between setup and action phases
TrackingDiagramOverride.StartAction();Setup separation: When
SeparateSetup = trueis set onReportConfigurationOptions, HTTP calls made beforeStartAction()are wrapped in a visual "Setup" partition in the diagram. CallStartSetup()to explicitly mark the beginning of the setup phase.
The Kronikol.MSTest package provides a CurrentTestInfo static class whose Fetcher property returns a Func<(string Name, string Id)> delegate that resolves the current test's identity. This is used by all extension CurrentTestInfoFetcher options:
using Kronikol.MSTest;
options.CurrentTestInfoFetcher = CurrentTestInfo.Fetcher;┌─────────────────────────────────┐
│ TestRun │ ← [TestClass] with [AssemblyInitialize]/[AssemblyCleanup]
│ : DiagrammedTestRun │ Generates reports in [AssemblyCleanup]
└─────────────────────────────────┘
│
▼
┌─────────────────────────────────┐
│ BaseFixture │ ← Creates tracked HttpClient
│ : DiagrammedComponentTest │ Sets async-local context on [TestInitialize]
│ IDisposable │ Enqueues MSTestScenarioInfo on [TestCleanup]
└─────────────┬───────────────────┘
│ inherited by
▼
┌─────────────────────────────────┐
│ Cake_Feature : BaseFixture │ ← Your test class with [TestMethod] methods
│ [TestClass] │
│ [Endpoint("/cake")] │
└─────────────────────────────────┘
- MSTest's
TestContextis not statically accessible like NUnit'sTestContext.CurrentContextor xUnit v3'sTestContext.Current. Kronikol.MSTest uses anAsyncLocal<TestContext>internally to make the test context available to the HTTP tracking pipeline. - The
[TestInitialize]and[TestCleanup]methods are provided byDiagrammedComponentTest. If your base fixture needs its own initialize/cleanup logic, call the base methods. - For data-driven tests (
[DataRow]), each data row is tracked as a separate scenario in the report.
| Property | Default | Description |
|---|---|---|
SpecificationsTitle |
"Service Specifications" |
Title shown at the top of reports |
PlantUmlServerBaseUrl |
"https://plantuml.com/plantuml" |
PlantUML server URL |
HtmlSpecificationsFileName |
"Specifications" |
Output filename for specs HTML |
HtmlTestRunReportFileName |
"TestRunReport" |
Output filename for test run HTML |
YamlSpecificationsFileName |
"Specifications" |
Output filename for YAML specs |
HtmlSpecificationsCustomStyleSheet |
Stylesheets.VioletThemeStyleSheet |
Custom CSS appended to specs HTML |
ExcludedHeaders |
[] |
HTTP headers to exclude from diagrams |
SeparateSetup |
false |
When true, HTTP calls made before StartAction() are wrapped in a visual "Setup" partition in the diagram |
HighlightSetup |
true |
When true (and SeparateSetup is enabled), the setup partition is rendered with a background colour |
| Property | Default | Description |
|---|---|---|
CallerName |
"Caller" |
Name of the calling service in diagrams |
FixedNameForReceivingService |
null |
When set, all downstream services use this name |
PortsToServiceNames |
{} |
Maps ports to human-readable downstream service names |
ClientNamesToServiceNames |
{} |
Maps named HTTP client names to service names |
When your SUT calls downstream HTTP services, those calls must flow through TestTrackingMessageHandler to produce proper HTTP-style diagram arrows (with method, status code, headers, body). Do not mock service client interfaces and use MessageTracker to manually log HTTP interactions — this produces event-style (blue) arrows that are misleading.
Recommended approaches:
-
In-memory fake APIs —
WebApplicationFactoryinstances that serve canned responses (see Example Project) -
JustEat HttpClient Interception — handler-level interception, chain with
TestTrackingMessageHandler -
WireMock.Net — real HTTP server on a random port, map in
PortsToServiceNames
See Tracking Dependencies#faking-dependencies-getting-proper-http-tracking for detailed examples of each approach.
Getting Started
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
Extensions
- Integration AtlasDataApi Extension
- Integration BigQuery Extension
- Integration Bigtable Extension
- Integration BlobStorage 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
- Search 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
- Diagnostics and Debugging
- CI Summary Integration
- CI Artifact Upload
Reference