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() { }
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 Attribute | xUnit Base | Discovery |
|---|---|---|
[Scenario] | FactAttribute | Single test case |
[ScenarioOutline] | TheoryAttribute | One test case per [Example] |
[Rule] | FactAttribute | Single test case |
[RuleOutline] | TheoryAttribute | One test case per [Example] |
[Example] | DataAttribute | Data 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 useSkip = "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 ...
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:
_Amatches parametera,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
FeatureTest— base class for BDD testsSpecificationTest— base class for MSpec tests[Example]— data rows for outlines- Step Methods —
Given(),When(),Then(),And(),But() - Best Practices — attribute usage patterns