Skip to main content

Attributes

Docs for xUnit SDK v0.4.0

LiveDoc provides custom attributes that integrate with xUnit's test discovery. Class-level attributes define containers (features, specifications). Method-level attributes define test cases that inherit from xUnit's [Fact] or [Theory].

using SweDevTools.LiveDoc.xUnit;

[Feature("Shopping Cart", Description = "Keeps cart contents consistent through the tested changes.")]
public class CartTests : FeatureTest
{
public CartTests(ITestOutputHelper output) : base(output) { }

[Scenario("Free shipping for large orders")]
public void Free_shipping() { /* ... */ }

[ScenarioOutline("Calculate tax for different regions")]
[Example("AU", 10.0)]
[Example("US", 0.0)]
public void Tax_calculation(string region, double rate) { /* ... */ }
}

Class-Level Attributes​

[Feature]​

Marks a test class as a BDD Feature. The class must inherit from FeatureTest.

[AttributeUsage(AttributeTargets.Class)]
public class FeatureAttribute : Attribute

Parameters​

  • name (optional): string — The feature title. If omitted, the class name is used with underscores converted to spaces.
  • Description (named, optional): string — Multi-line description displayed in formatted output.
// Explicit title
[Feature("User Registration")]
public class RegistrationTests : FeatureTest { }

// Auto-derived title: "Registration Tests"
[Feature]
public class Registration_Tests : FeatureTest { }

// With description
[Feature("Order Processing", Description = @"
Helps callers distinguish accepted from rejected
orders before any further processing.")]
public class OrderTests : FeatureTest { }

[Specification]​

Marks a test class as an MSpec Specification. The class must inherit from SpecificationTest.

[AttributeUsage(AttributeTargets.Class)]
public class SpecificationAttribute : Attribute

Parameters​

  • title (optional): string — The specification title. If omitted, the class name is used.
  • Description (named, optional): string — Description displayed in formatted output.
[Specification("Email Validation", Description = @"
Helps callers reject malformed addresses
before using them.")]
public class EmailSpec : SpecificationTest { }

Method-Level Attributes​

[Scenario]​

Marks a method as a single BDD scenario. Inherits from xUnit's FactAttribute, so xUnit discovers it automatically.

public class ScenarioAttribute : FactAttribute

Parameters​

  • testMethodName (optional): string — Display name in Test Explorer. Defaults to the method name via [CallerMemberName].
  • Description (named, optional): string — Additional context shown in formatted output.
// Auto-named from method: "User logs in successfully"
[Scenario]
public void User_logs_in_successfully() { }

// Explicit display name
[Scenario("User logs in with valid credentials")]
public void User_logs_in_successfully() { }

// With description
[Scenario(Description = "Limited to registered users with valid credentials.")]
public void User_logs_in_successfully() { }

// Both title and description
[Scenario("Login flow", Description = "Happy path for registered users")]
public void User_logs_in_successfully() { }
xUnit inheritance

Because [Scenario] inherits from [Fact], xUnit discovers and runs these methods with no custom test runner. They appear in Test Explorer alongside regular [Fact] tests.

[ScenarioOutline]​

Marks a method as a data-driven BDD scenario. Inherits from xUnit's TheoryAttribute. Requires one or more [Example] attributes.

public class ScenarioOutlineAttribute : TheoryAttribute

Parameters​

  • testMethodName (optional): string — Display name. Defaults to method name.
  • Description (named, optional): string — Description for formatted output.
[ScenarioOutline]
[Example("Alice", true)]
[Example("Unknown", false)]
public void User_authentication(string username, bool expected) { }

[ScenarioOutline("Validate shipping rates")]
[Example("AU", 100, "Free")]
[Example("NZ", 100, "International")]
public void Shipping_rates(string country, decimal total, string type) { }

[ScenarioOutline(Description = "Covers only the listed regions; other rates need separate examples.")]
[Example("AU", 10.0)]
[Example("US", 0.0)]
public void Tax_rates(string region, double rate) { }

[Rule]​

Marks a method as a single MSpec rule. Inherits from xUnit's FactAttribute. Used in classes inheriting SpecificationTest.

public class RuleAttribute : FactAttribute

Parameters​

  • title (optional): string — The rule title with embedded values. If omitted, the method name is used.
  • Description (named, optional): string — Additional description shown in output.

Titles supplied through nameof(...) use identifier formatting, so underscores become spaces. Authored string literals preserve underscores:

[Rule(nameof(My_rule_name_is_a_description))] // "My rule name is a description"
[Rule("LIVEDOC_RUN_TYPE")] // "LIVEDOC_RUN_TYPE"
// Title with embedded values
[Rule("Adding '5' and '3' returns '8'")]
public void Addition() { }

// Auto-named from method
[Rule]
public void Adding_positive_numbers_increases_the_total() { }

// With named parameters
[Rule("Subtracting <b:3> from <a:10> returns <result:7>")]
public void Subtraction() { }

[Rule(
"The selected policy is '<policy>'",
Description = "The selected policy is shown with its supporting context.")]
public void Policy() { }

[RuleOutline]​

Marks a method as a data-driven MSpec rule. Inherits from xUnit's TheoryAttribute. Requires [Example] attributes.

public class RuleOutlineAttribute : TheoryAttribute

Parameters​

  • title (optional): string — Rule title with <placeholder> segments for display.
  • Description (named, optional): string — Additional description.
[RuleOutline("Adding '<a>' and '<b>' returns '<result>'")]
[Example(1, 2, 3)]
[Example(5, 5, 10)]
[Example(-1, 1, 0)]
public void Addition_examples(int a, int b, int result) { }

[RuleOutline(
"Policy '<policy>' is selected",
Description = "The <policy> policy applies to this example row.")]
[Example("express")]
public void Policy_examples(string policy) { }

The same nameof(...) formatting applies to [Feature], [Specification], [Scenario], [ScenarioOutline], and [RuleOutline].

[Example]​

Provides data rows for [ScenarioOutline] and [RuleOutline]. Inherits from xUnit's DataAttribute. See [Example] Attribute for full details.

[Example("Australia", 100.00, "Free")]

Attribute Inheritance from xUnit​

LiveDoc attributes extend xUnit's native attributes, ensuring full compatibility with all xUnit tooling:

LiveDoc AttributexUnit BaseDiscovery
[Scenario]FactAttributeSingle test case
[ScenarioOutline]TheoryAttributeOne test case per [Example]
[Rule]FactAttributeSingle test case
[RuleOutline]TheoryAttributeOne test case per [Example]
[Example]DataAttributeData source for Theory

This means:

  • Tests appear in Test Explorer without custom adapters
  • All xUnit runners (dotnet test, VS, Rider) work automatically
  • [Scenario] tests can use Skip = "reason" just like [Fact]
  • [ScenarioOutline] tests support xUnit's theory data pipeline
// Skip a scenario (inherited from FactAttribute)
[Scenario(Skip = "Pending implementation")]
public void Upcoming_feature() { }

Description Property​

All attributes accept a Description named property. Descriptions appear in the formatted test output and LiveDoc Viewer, providing valuable context:

[Feature("Checkout Flow", Description = @"
Keeps coupon adjustments predictable for the
checkout cases covered below.")]
public class CheckoutTests : FeatureTest
{
public CheckoutTests(ITestOutputHelper output) : base(output) { }

[Scenario("Apply coupon code", Description = @"
Covers the accepted and expired coupon cases
represented by this scenario.")]
public void Apply_coupon() { /* ... */ }
}

Output:

Feature: Checkout Flow
Keeps coupon adjustments predictable for the
checkout cases covered below.

Scenario: Apply coupon code
Covers the accepted and expired coupon cases
represented by this scenario.

Given ...
When ...
Then ...
Best practice

Prefer a purpose-first Description on [Feature] and [Specification] when it adds reader value. It is optional, and the wording should not claim outcomes beyond the scenarios or rules.


Method Name Conventions​

Automatic title derivation​

When no explicit title is provided, the method name is converted:

  • Underscores (_) become spaces
  • Example: User_logs_in_successfully → "User logs in successfully"

_ALLCAPS placeholder segments​

In [ScenarioOutline] and [RuleOutline], _ALLCAPS segments in method names become <placeholder> markers matched to method parameters:

[RuleOutline]
[Example(10, 2, 5)]
[Example(100, 10, 10)]
public void Dividing_A_by_B_returns_RESULT(int a, int b, int result)
{
Assert.Equal(result, a / b);
}
// Output: "Dividing '10' by '2' returns '5'"

Matching rules:

  • _A matches parameter a, A, or _a (case-insensitive)
  • Unmatched ALLCAPS segments remain as literal text
  • This is an alternative to providing an explicit title with <param> placeholders

See Also​