-
Notifications
You must be signed in to change notification settings - Fork 1
Tabular Attributes
Added in v2.32.0
Tabular attributes let you define data-driven tests with typed input and output rows directly on your test methods — no extra data classes, no CSV files, no ceremony. Column names are declared inline, rows are positionally aligned, and output verification is built in.
using TestTrackingDiagrams.TabularAttributes;
public class GreetingTests
{
[Theory]
[HeadIn("Name", "Age"), HeadOut("Message")]
[Inputs("Alice", 30), Outputs("Hello Alice")]
[Inputs("Bob", 25), Outputs("Hello Bob")]
public void Greeting_matches_name(
TabularInputs<Person> inputs, TabularOutputs<Greeting> outputs)
{
foreach (var person in inputs)
{
var result = Greet(person);
outputs.AddActual(result);
}
outputs.Verify();
}
private Greeting Greet(Person p) => new() { Message = $"Hello {p.Name}" };
}
public class Person
{
public string Name { get; set; } = "";
public int Age { get; set; }
}
public class Greeting
{
public string Message { get; set; } = "";
}-
[HeadIn("Name", "Age")]declares the input column names and acts as the data source (replaces[InlineData]/[MemberData]). -
[HeadOut("Message")]declares the output column names. -
[Inputs("Alice", 30)]defines one row of input data. Add as many as you need. -
[Outputs("Hello Alice")]defines the corresponding expected output row. -
TabularInputs<Person>is anIReadOnlyList<Person>— iterate withforeachto get per-row diagram delimiters. -
TabularOutputs<Greeting>collects actual results viaAddActual()and verifies them against expected rows viaVerify().
| Attribute | Target | AllowMultiple | Purpose |
|---|---|---|---|
[Inputs(...)] |
Method | Yes | One row of input values (positional, matching [HeadIn] columns) |
[Outputs(...)] |
Method | Yes | One row of expected output values (positional, matching [HeadOut] columns) |
[HeadOut(...)] |
Method | No | Declares output column names |
| Framework | Package | Base Type |
|---|---|---|
| xUnit v3 | TestTrackingDiagrams.xUnit3 |
Xunit.v3.DataAttribute |
| xUnit v2 | TestTrackingDiagrams.xUnit2 |
Xunit.Sdk.DataAttribute |
| NUnit 4 | TestTrackingDiagrams.NUnit4 |
NUnitAttribute, ITestBuilder |
| MSTest | TestTrackingDiagrams.MSTest |
Attribute, ITestDataSource |
All [HeadIn] attributes live in namespace TestTrackingDiagrams.TabularAttributes, so a single using covers everything.
IReadOnlyList<T> containing deserialized input rows. Iterating with foreach automatically emits per-row diagram delimiters (hnote annotations that visually separate each row's interactions in the sequence diagram).
// Access by index
var first = inputs[0];
// Iterate (emits diagram delimiters)
foreach (var person in inputs)
{
await sut.Process(person);
}IReadOnlyList<T> containing expected output rows. After processing, call AddActual() for each actual result and then Verify() to compare.
// Record actual output
outputs.AddActual(result);
// Verify all rows (position-based matching)
outputs.Verify(); // throws TabularVerificationException on mismatchVerification rules:
- Matching rows: expected and actual at the same position are compared cell by cell.
- Surplus rows: more actuals than expected → fail.
- Missing rows: fewer actuals than expected → fail.
Column names are matched to T's public properties using a sanitization process:
| Column Name | Matches Property |
|---|---|
"Name" |
Name |
"First Name" |
FirstName (spaces removed) |
"Name & Age" |
NameAndAge (& → And) |
"username" |
USERNAME (case-insensitive) |
If [HeadIn] is used without arguments ([HeadIn]), column names are inferred from T's public property names.
[Theory]
[HeadIn("Name", "Age")]
[Inputs("Alice", 30)]
public void My_test(TabularInputs<Person> inputs) { ... }[Theory]
[HeadIn("Name", "Age")]
[Inputs("Alice", 30)]
public void My_test(TabularInputs<Person> inputs) { ... }[Test]
[HeadIn("Name", "Age")]
[Inputs("Alice", 30)]
public void My_test(TabularInputs<Person> inputs) { ... }[TestMethod]
[HeadIn("Name", "Age")]
[Inputs("Alice", 30)]
public void My_test(TabularInputs<Person> inputs) { ... }When TabularInputs<T> or TabularOutputs<T> is passed as a step parameter, StepCollector automatically detects it and renders it as a tabular parameter in the report (instead of inline text):
Track.That.Given("the test data", "inputs", inputs);This produces a table in the step parameters showing all rows and columns.
You can use TabularInputs<T> without outputs:
[Theory]
[HeadIn("Name", "Age")]
[Inputs("Alice", 30)]
[Inputs("Bob", 25)]
public void Process_people(TabularInputs<Person> inputs)
{
foreach (var person in inputs)
{
sut.Process(person);
}
}You can also use only TabularOutputs<T>:
[Theory]
[HeadIn, HeadOut("Name", "Status")]
[Outputs("Alice", "Active")]
[Outputs("Bob", "Inactive")]
public void Verify_results(TabularOutputs<UserStatus> outputs)
{
outputs.AddActual(new UserStatus { Name = "Alice", Status = "Active" });
outputs.AddActual(new UserStatus { Name = "Bob", Status = "Inactive" });
outputs.Verify();
}Tabular attributes batch multiple data rows into a single test execution. This is useful when:
- You want to verify a batch operation processes all rows correctly.
- You want per-row diagram delimiters showing each row's interactions.
- You want to compare expected vs actual outputs positionally.
For truly independent test cases (where each row should be a separate test run with its own pass/fail), use the framework's native parameterisation ([InlineData], [TestCase], etc.) instead.
- Step Tracking — BDD-style step methods
- Generated Reports — how tabular parameters appear in reports
- Assertion Tracking — tracking assertion pass/fail
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 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
- 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
- Merging Parallel Reports
Reference