diff --git a/TUnit.Core/Attributes/BaseTestAttribute.cs b/TUnit.Core/Attributes/BaseTestAttribute.cs
index 9ba5512753..22b5b808df 100644
--- a/TUnit.Core/Attributes/BaseTestAttribute.cs
+++ b/TUnit.Core/Attributes/BaseTestAttribute.cs
@@ -1,12 +1,24 @@
namespace TUnit.Core;
-// Any new test type attributes should inherit from this
-// This ensures we have a location of the test provided by the compiler
-// Using [CallerLineNumber] [CallerFilePath]
+///
+/// Base class for test method attributes. Automatically captures the source file path and line number
+/// where the test is defined, using compiler services.
+///
+///
+/// Inherit from this class to create custom test type attributes. The and
+/// properties are populated automatically by the compiler via [CallerFilePath] and [CallerLineNumber].
+///
[AttributeUsage(AttributeTargets.Method)]
public abstract class BaseTestAttribute : TUnitAttribute
{
+ ///
+ /// Gets the source file path where the test is defined.
+ ///
public readonly string File;
+
+ ///
+ /// Gets the line number in the source file where the test is defined.
+ ///
public readonly int Line;
internal BaseTestAttribute(string file, int line)
diff --git a/TUnit.Core/Attributes/ClassConstructorSourceAttribute.cs b/TUnit.Core/Attributes/ClassConstructorSourceAttribute.cs
index d4d89fe7cd..7a8926f882 100644
--- a/TUnit.Core/Attributes/ClassConstructorSourceAttribute.cs
+++ b/TUnit.Core/Attributes/ClassConstructorSourceAttribute.cs
@@ -3,9 +3,44 @@
namespace TUnit.Core;
+///
+/// Specifies a custom to use when creating test class instances.
+/// This enables dependency injection and custom object creation for test classes.
+///
+///
+///
+/// Can be applied at the class level (affecting only that class) or at the assembly level
+/// (affecting all test classes in the assembly).
+///
+///
+/// The specified type must implement and have a parameterless constructor.
+/// For strongly-typed usage, prefer .
+///
+///
+///
+///
+/// [ClassConstructor<DependencyInjectionClassConstructor>]
+/// public class MyTests
+/// {
+/// private readonly IMyService _service;
+///
+/// public MyTests(IMyService service)
+/// {
+/// _service = service;
+/// }
+///
+/// [Test]
+/// public void TestWithInjectedDependency() { }
+/// }
+///
+///
[AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Class)]
public class ClassConstructorAttribute : TUnitAttribute
{
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The type that implements .
public ClassConstructorAttribute(
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.PublicMethods)]
Type classConstructorType)
@@ -13,10 +48,18 @@ public ClassConstructorAttribute(
ClassConstructorType = classConstructorType;
}
+ ///
+ /// Gets or sets the type that implements and is used to create test class instances.
+ ///
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.PublicMethods)]
public Type ClassConstructorType { get; init; }
}
+///
+/// Specifies a custom to use when creating test class instances.
+/// Generic version that provides compile-time type safety.
+///
+/// The type that implements .
[AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Class)]
public sealed class ClassConstructorAttribute<
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.PublicMethods)] T>()
diff --git a/TUnit.Core/Attributes/DynamicTestBuilderAttribute.cs b/TUnit.Core/Attributes/DynamicTestBuilderAttribute.cs
index 5592b90cad..2caede647b 100644
--- a/TUnit.Core/Attributes/DynamicTestBuilderAttribute.cs
+++ b/TUnit.Core/Attributes/DynamicTestBuilderAttribute.cs
@@ -3,4 +3,8 @@
namespace TUnit.Core;
+///
+/// Marks a method as a dynamic test builder that programmatically generates test cases at runtime.
+/// Methods decorated with this attribute can yield test definitions dynamically.
+///
public class DynamicTestBuilderAttribute([CallerFilePath] string file = "", [CallerLineNumber] int line = 0) : BaseTestAttribute(file, line);
diff --git a/TUnit.Core/Attributes/TUnitAttribute.cs b/TUnit.Core/Attributes/TUnitAttribute.cs
index 4a40e36c44..2bf2330764 100644
--- a/TUnit.Core/Attributes/TUnitAttribute.cs
+++ b/TUnit.Core/Attributes/TUnitAttribute.cs
@@ -1,5 +1,9 @@
namespace TUnit.Core;
+///
+/// Base class for all TUnit framework attributes.
+/// This class cannot be instantiated directly; use one of the derived attribute types.
+///
public class TUnitAttribute : Attribute
{
internal TUnitAttribute()
diff --git a/TUnit.Core/Attributes/TestData/ArgumentsAttribute.cs b/TUnit.Core/Attributes/TestData/ArgumentsAttribute.cs
index 8ad3d88d69..ca1ad191ce 100644
--- a/TUnit.Core/Attributes/TestData/ArgumentsAttribute.cs
+++ b/TUnit.Core/Attributes/TestData/ArgumentsAttribute.cs
@@ -32,8 +32,15 @@ namespace TUnit.Core;
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Parameter, AllowMultiple = true)]
public sealed class ArgumentsAttribute : Attribute, IDataSourceAttribute, ITestRegisteredEventReceiver
{
+ ///
+ /// Gets the array of argument values to pass to the test method.
+ ///
public object?[] Values { get; }
+ ///
+ /// Gets or sets a reason to skip this specific test case.
+ /// When set, the test case will be skipped with the given reason.
+ ///
public string? Skip { get; set; }
///
@@ -60,6 +67,10 @@ public sealed class ArgumentsAttribute : Attribute, IDataSourceAttribute, ITestR
///
public bool SkipIfEmpty { get; set; }
+ ///
+ /// Initializes a new instance of the class with the specified test argument values.
+ ///
+ /// The argument values to pass to the test method. Pass null for a single null argument.
public ArgumentsAttribute(params object?[]? values)
{
if (values == null)
@@ -112,9 +123,26 @@ public ValueTask OnTestRegistered(TestRegisteredContext context)
public int Order => 0;
}
+///
+/// Provides a strongly-typed inline value for a parameterized test with a single parameter.
+///
+/// The type of the test parameter.
+/// The value to pass to the test method.
+///
+///
+/// [Test]
+/// [Arguments<string>("hello")]
+/// [Arguments<string>("world")]
+/// public void TestWithString(string input) { }
+///
+///
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Parameter, AllowMultiple = true)]
public sealed class ArgumentsAttribute(T value) : TypedDataSourceAttribute, ITestRegisteredEventReceiver
{
+ ///
+ /// Gets or sets a reason to skip this specific test case.
+ /// When set, the test case will be skipped with the given reason.
+ ///
public string? Skip { get; set; }
///
diff --git a/TUnit.Core/Attributes/TestData/ClassDataSourceAttribute.cs b/TUnit.Core/Attributes/TestData/ClassDataSourceAttribute.cs
index 9dbfc8ff2c..2d87437f55 100644
--- a/TUnit.Core/Attributes/TestData/ClassDataSourceAttribute.cs
+++ b/TUnit.Core/Attributes/TestData/ClassDataSourceAttribute.cs
@@ -3,6 +3,33 @@
namespace TUnit.Core;
+///
+/// Provides test data by creating instances of one or more specified types.
+/// The instances are created using their constructors and can optionally be shared across tests.
+///
+///
+///
+/// Use this attribute to inject class instances as test method parameters or constructor arguments.
+/// The attribute supports sharing instances across tests via the property and
+/// keyed sharing via the property.
+///
+///
+/// For strongly-typed single-parameter usage, prefer the generic version .
+///
+///
+///
+///
+/// // Create a new instance for each test
+/// [Test]
+/// [ClassDataSource(typeof(MyService))]
+/// public void TestWithService(MyService service) { }
+///
+/// // Share the instance across all tests in the class
+/// [Test]
+/// [ClassDataSource(typeof(MyService), Shared = [SharedType.PerClass])]
+/// public void TestWithSharedService(MyService service) { }
+///
+///
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Parameter, AllowMultiple = true)]
public sealed class ClassDataSourceAttribute : UntypedDataSourceGeneratorAttribute
{
@@ -74,7 +101,16 @@ public ClassDataSourceAttribute(params Type[] types)
_types = types;
}
+ ///
+ /// Gets or sets how instances are shared across tests, one per type parameter.
+ /// Defaults to (a new instance per test).
+ ///
public SharedType[] Shared { get; set; } = [SharedType.None];
+
+ ///
+ /// Gets or sets the sharing keys, one per type parameter.
+ /// Used when is set to to identify shared instances.
+ ///
public string[] Keys { get; set; } = [];
[UnconditionalSuppressMessage("Trimming", "IL2062:The parameter of method has a DynamicallyAccessedMembersAttribute, but the value passed to it can not be statically analyzed.",
@@ -121,6 +157,27 @@ public ClassDataSourceAttribute(params Type[] types)
}
+///
+/// Provides test data by creating an instance of .
+/// The instance is created using its constructor and can optionally be shared across tests.
+///
+/// The type to instantiate as test data.
+///
+///
+/// Use the property to control instance sharing:
+/// (default) creates a new instance per test,
+/// shares within the test class,
+/// shares across the assembly,
+/// shares by a specified .
+///
+///
+///
+///
+/// [Test]
+/// [ClassDataSource<MyService>(Shared = SharedType.PerClass)]
+/// public void TestWithService(MyService service) { }
+///
+///
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Parameter, AllowMultiple = true)]
public sealed class ClassDataSourceAttribute<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors | DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties)] T>
: DataSourceGeneratorAttribute
diff --git a/TUnit.Core/Attributes/TestData/IDataSourceAttribute.cs b/TUnit.Core/Attributes/TestData/IDataSourceAttribute.cs
index 04835fe5c7..9dd5122933 100644
--- a/TUnit.Core/Attributes/TestData/IDataSourceAttribute.cs
+++ b/TUnit.Core/Attributes/TestData/IDataSourceAttribute.cs
@@ -1,7 +1,17 @@
namespace TUnit.Core;
+///
+/// Defines a data source that provides test data for parameterized tests.
+/// Implement this interface to create custom data source attributes.
+///
public interface IDataSourceAttribute
{
+ ///
+ /// Asynchronously generates data rows for parameterized tests.
+ /// Each yielded function, when invoked, produces one set of arguments for a test invocation.
+ ///
+ /// Metadata about the test and parameters being generated.
+ /// An async enumerable of factory functions that produce test data rows.
public IAsyncEnumerable>> GetDataRowsAsync(DataGeneratorMetadata dataGeneratorMetadata);
///
diff --git a/TUnit.Core/Attributes/TestData/MatrixDataSourceAttribute.cs b/TUnit.Core/Attributes/TestData/MatrixDataSourceAttribute.cs
index ce19c143dd..503c90f249 100644
--- a/TUnit.Core/Attributes/TestData/MatrixDataSourceAttribute.cs
+++ b/TUnit.Core/Attributes/TestData/MatrixDataSourceAttribute.cs
@@ -4,6 +4,36 @@
namespace TUnit.Core;
+///
+/// Generates test cases from all combinations (Cartesian product) of parameter values.
+///
+///
+///
+/// For boolean parameters, all values (true, false) are generated automatically.
+/// For enum parameters, all defined enum values are generated automatically.
+/// For other types, use [Matrix(...)] on individual parameters to specify the values.
+///
+///
+/// Use on the test method to exclude specific combinations.
+///
+///
+///
+///
+/// [Test, MatrixDataSource]
+/// public void TestAllCombinations(
+/// [Matrix(1, 2, 3)] int x,
+/// [Matrix("a", "b")] string y)
+/// {
+/// // Generates 6 test cases: (1,"a"), (1,"b"), (2,"a"), (2,"b"), (3,"a"), (3,"b")
+/// }
+///
+/// [Test, MatrixDataSource]
+/// public void TestWithEnum(bool enabled, MyEnum mode)
+/// {
+/// // Automatically generates all bool x enum combinations
+/// }
+///
+///
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public sealed class MatrixDataSourceAttribute : UntypedDataSourceGeneratorAttribute, IAccessesInstanceData
{
diff --git a/TUnit.Core/Attributes/TestData/MethodDataSourceAttribute.cs b/TUnit.Core/Attributes/TestData/MethodDataSourceAttribute.cs
index 2d896b5aa3..a2fa77b852 100644
--- a/TUnit.Core/Attributes/TestData/MethodDataSourceAttribute.cs
+++ b/TUnit.Core/Attributes/TestData/MethodDataSourceAttribute.cs
@@ -5,12 +5,57 @@
namespace TUnit.Core;
+///
+/// Provides test data from a method, property, or field on the specified type .
+///
+/// The type containing the data source member.
+/// The name of the method, property, or field that provides the test data.
+///
+///
+/// [Test]
+/// [MethodDataSource<TestDataProvider>(nameof(TestDataProvider.GetTestCases))]
+/// public void MyTest(int input, string expected) { }
+///
+///
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Parameter, AllowMultiple = true)]
public class MethodDataSourceAttribute<
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods)]
T>(string methodNameProvidingDataSource)
: MethodDataSourceAttribute(typeof(T), methodNameProvidingDataSource);
+///
+/// Provides test data from a method, property, or field in the test class or a specified type.
+///
+///
+///
+/// The data source can be a method, property, or field that returns test data.
+/// Supported return types include single values, , ,
+/// , tuples, and arrays.
+///
+///
+/// When no class type is specified, the data source is looked up in the test class itself.
+/// Both static and instance members are supported.
+///
+///
+///
+///
+/// // Using a method in the same class
+/// [Test]
+/// [MethodDataSource(nameof(GetTestData))]
+/// public void MyTest(int value, string name) { }
+///
+/// public static IEnumerable<(int, string)> GetTestData()
+/// {
+/// yield return (1, "one");
+/// yield return (2, "two");
+/// }
+///
+/// // Using a method in another class
+/// [Test]
+/// [MethodDataSource(typeof(SharedData), nameof(SharedData.GetValues))]
+/// public void MyTest(string value) { }
+///
+///
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Parameter, AllowMultiple = true)]
public class MethodDataSourceAttribute : Attribute, IDataSourceAttribute
{
@@ -20,12 +65,27 @@ public class MethodDataSourceAttribute : Attribute, IDataSourceAttribute
| System.Reflection.BindingFlags.Instance
| System.Reflection.BindingFlags.FlattenHierarchy;
+ ///
+ /// Gets the type containing the data source member, or null if the data source is in the test class itself.
+ ///
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods)]
public Type? ClassProvidingDataSource { get; }
+
+ ///
+ /// Gets the name of the method, property, or field that provides the test data.
+ ///
public string MethodNameProvidingDataSource { get; }
+ ///
+ /// Gets or sets an AOT-safe factory function for providing test data programmatically.
+ /// When set, this factory is used instead of reflection-based member lookup.
+ ///
public Func>>>? Factory { get; set; }
+ ///
+ /// Gets or sets the arguments to pass to the data source method.
+ /// Use this when the data source method requires parameters.
+ ///
public object?[] Arguments { get; set; } = [];
///
diff --git a/TUnit.Core/Attributes/TestData/SharedType.cs b/TUnit.Core/Attributes/TestData/SharedType.cs
index 0bb7f57294..fb05203b8a 100644
--- a/TUnit.Core/Attributes/TestData/SharedType.cs
+++ b/TUnit.Core/Attributes/TestData/SharedType.cs
@@ -1,10 +1,34 @@
namespace TUnit.Core;
+///
+/// Specifies how class data source instances are shared across tests.
+/// Used with and .
+///
public enum SharedType
{
+ ///
+ /// A new instance is created for each test (no sharing). This is the default.
+ ///
None,
+
+ ///
+ /// The instance is shared across all tests within the same test class.
+ ///
PerClass,
+
+ ///
+ /// The instance is shared across all tests within the same assembly.
+ ///
PerAssembly,
+
+ ///
+ /// The instance is shared across all tests in the entire test session.
+ ///
PerTestSession,
+
+ ///
+ /// The instance is shared across tests that specify the same key.
+ /// Use the Key or Keys property on the data source attribute to specify the key.
+ ///
Keyed,
}
diff --git a/TUnit.Core/Attributes/TestHooks/AfterAttribute.cs b/TUnit.Core/Attributes/TestHooks/AfterAttribute.cs
index 6d2df6047e..817acf0b17 100644
--- a/TUnit.Core/Attributes/TestHooks/AfterAttribute.cs
+++ b/TUnit.Core/Attributes/TestHooks/AfterAttribute.cs
@@ -4,5 +4,39 @@
namespace TUnit.Core;
+///
+/// Marks a method as a teardown hook that runs after a specific scope (test, class, assembly, or test session).
+/// The hook is scoped to the class that declares it.
+///
+///
+///
+/// Use [After(HookType.Test)] to run the method after each test in the declaring class.
+/// Use [After(HookType.Class)] to run the method once after all tests in the class complete.
+/// Use [After(HookType.Assembly)] to run the method once after all tests in the assembly complete.
+/// Use [After(HookType.TestSession)] to run the method once per test session teardown.
+///
+///
+/// For hooks that apply globally to every test regardless of class, use instead.
+///
+///
+///
+///
+/// public class MyTests
+/// {
+/// [After(HookType.Test)]
+/// public void CleanUp()
+/// {
+/// // Runs after each test in this class
+/// }
+///
+/// [After(HookType.Class)]
+/// public static void ClassCleanUp()
+/// {
+/// // Runs once after all tests in this class complete
+/// }
+/// }
+///
+///
+/// The scope at which this hook runs.
[AttributeUsage(AttributeTargets.Method)]
public sealed class AfterAttribute(HookType hookType, [CallerFilePath] string file = "", [CallerLineNumber] int line = 0) : HookAttribute(hookType, file, line);
diff --git a/TUnit.Core/Attributes/TestHooks/AfterEveryAttribute.cs b/TUnit.Core/Attributes/TestHooks/AfterEveryAttribute.cs
index 8ad2189d3e..d0796a9e8d 100644
--- a/TUnit.Core/Attributes/TestHooks/AfterEveryAttribute.cs
+++ b/TUnit.Core/Attributes/TestHooks/AfterEveryAttribute.cs
@@ -2,6 +2,39 @@
namespace TUnit.Core;
+///
+/// Marks a method as a global teardown hook that runs after every test, class, assembly, or test session,
+/// regardless of which class declares it.
+///
+///
+///
+/// Unlike which is scoped to the declaring class,
+/// [AfterEvery] applies globally. For example, [AfterEvery(HookType.Test)] runs after
+/// every test in the entire test suite, not just tests in the declaring class.
+///
+///
+/// The method must be static and declared in a class. It will be invoked for all tests matching the specified scope.
+///
+///
+///
+///
+/// public class GlobalHooks
+/// {
+/// [AfterEvery(HookType.Test)]
+/// public static void AfterEachTest(TestContext context)
+/// {
+/// // Runs after every test in the entire suite
+/// }
+///
+/// [AfterEvery(HookType.Class)]
+/// public static void AfterEachClass(ClassHookContext context)
+/// {
+/// // Runs after every test class
+/// }
+/// }
+///
+///
+/// The scope at which this hook runs.
#pragma warning disable CS9113
[AttributeUsage(AttributeTargets.Method)]
public sealed class AfterEveryAttribute(HookType hookType, [CallerFilePath] string file = "", [CallerLineNumber] int line = 0) : HookAttribute(hookType, file, line);
diff --git a/TUnit.Core/Attributes/TestHooks/BeforeAttribute.cs b/TUnit.Core/Attributes/TestHooks/BeforeAttribute.cs
index 244c39f6ed..a9a9a07ffe 100644
--- a/TUnit.Core/Attributes/TestHooks/BeforeAttribute.cs
+++ b/TUnit.Core/Attributes/TestHooks/BeforeAttribute.cs
@@ -4,5 +4,39 @@
namespace TUnit.Core;
+///
+/// Marks a method as a setup hook that runs before a specific scope (test, class, assembly, or test session).
+/// The hook is scoped to the class that declares it.
+///
+///
+///
+/// Use [Before(HookType.Test)] to run the method before each test in the declaring class.
+/// Use [Before(HookType.Class)] to run the method once before all tests in the class.
+/// Use [Before(HookType.Assembly)] to run the method once before all tests in the assembly.
+/// Use [Before(HookType.TestSession)] to run the method once per test session.
+///
+///
+/// For hooks that apply globally to every test regardless of class, use instead.
+///
+///
+///
+///
+/// public class MyTests
+/// {
+/// [Before(HookType.Test)]
+/// public void SetUp()
+/// {
+/// // Runs before each test in this class
+/// }
+///
+/// [Before(HookType.Class)]
+/// public static void ClassSetUp()
+/// {
+/// // Runs once before all tests in this class
+/// }
+/// }
+///
+///
+/// The scope at which this hook runs.
[AttributeUsage(AttributeTargets.Method)]
public sealed class BeforeAttribute(HookType hookType, [CallerFilePath] string file = "", [CallerLineNumber] int line = 0) : HookAttribute(hookType, file, line);
diff --git a/TUnit.Core/Attributes/TestHooks/BeforeEveryAttribute.cs b/TUnit.Core/Attributes/TestHooks/BeforeEveryAttribute.cs
index 584f582141..1d81fe0df7 100644
--- a/TUnit.Core/Attributes/TestHooks/BeforeEveryAttribute.cs
+++ b/TUnit.Core/Attributes/TestHooks/BeforeEveryAttribute.cs
@@ -2,6 +2,39 @@
namespace TUnit.Core;
+///
+/// Marks a method as a global setup hook that runs before every test, class, assembly, or test session,
+/// regardless of which class declares it.
+///
+///
+///
+/// Unlike which is scoped to the declaring class,
+/// [BeforeEvery] applies globally. For example, [BeforeEvery(HookType.Test)] runs before
+/// every test in the entire test suite, not just tests in the declaring class.
+///
+///
+/// The method must be static and declared in a class. It will be invoked for all tests matching the specified scope.
+///
+///
+///
+///
+/// public class GlobalHooks
+/// {
+/// [BeforeEvery(HookType.Test)]
+/// public static void BeforeEachTest(TestContext context)
+/// {
+/// // Runs before every test in the entire suite
+/// }
+///
+/// [BeforeEvery(HookType.Class)]
+/// public static void BeforeEachClass(ClassHookContext context)
+/// {
+/// // Runs before every test class
+/// }
+/// }
+///
+///
+/// The scope at which this hook runs.
#pragma warning disable CS9113
[AttributeUsage(AttributeTargets.Method)]
public sealed class BeforeEveryAttribute(HookType hookType, [CallerFilePath] string file = "", [CallerLineNumber] int line = 0) : HookAttribute(hookType, file, line);
diff --git a/TUnit.Core/Attributes/TestHooks/HookAttribute.cs b/TUnit.Core/Attributes/TestHooks/HookAttribute.cs
index 6e8755511f..7cd2cf598d 100644
--- a/TUnit.Core/Attributes/TestHooks/HookAttribute.cs
+++ b/TUnit.Core/Attributes/TestHooks/HookAttribute.cs
@@ -1,9 +1,27 @@
namespace TUnit.Core;
+///
+/// Base class for hook attributes (, ,
+/// , ).
+///
+///
+/// This class is not intended to be used directly. Use the derived attributes instead.
+///
public class HookAttribute : TUnitAttribute
{
+ ///
+ /// Gets the scope at which this hook runs (Test, Class, Assembly, TestSession, or TestDiscovery).
+ ///
public HookType HookType { get; }
+
+ ///
+ /// Gets the source file path where this hook is defined.
+ ///
public string File { get; }
+
+ ///
+ /// Gets the line number in the source file where this hook is defined.
+ ///
public int Line { get; }
internal HookAttribute(HookType hookType, string file, int line)
@@ -18,5 +36,9 @@ internal HookAttribute(HookType hookType, string file, int line)
Line = line;
}
+ ///
+ /// Gets or sets the execution order of this hook relative to other hooks at the same scope.
+ /// Lower values execute first. Default is 0.
+ ///
public int Order { get; init; }
}
diff --git a/TUnit.Core/Attributes/TestMetadata/ExecutionPriorityAttribute.cs b/TUnit.Core/Attributes/TestMetadata/ExecutionPriorityAttribute.cs
index cf6077d71a..08c3fb42bb 100644
--- a/TUnit.Core/Attributes/TestMetadata/ExecutionPriorityAttribute.cs
+++ b/TUnit.Core/Attributes/TestMetadata/ExecutionPriorityAttribute.cs
@@ -3,12 +3,41 @@
namespace TUnit.Core;
+///
+/// Sets the execution priority for a test, class, or assembly.
+/// Higher priority tests are scheduled to execute before lower priority tests.
+///
+///
+/// Priority values range from (runs last) to
+/// (runs first). The default is .
+///
+///
+///
+/// [Test]
+/// [ExecutionPriority(Priority.High)]
+/// public void ImportantTest() { }
+///
+/// [Test]
+/// [ExecutionPriority(Priority.Low)]
+/// public void LessImportantTest() { }
+///
+///
+/// The execution priority level.
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class | AttributeTargets.Assembly)]
public class ExecutionPriorityAttribute : SingleTUnitAttribute, ITestDiscoveryEventReceiver, IScopedAttribute
{
+ ///
+ /// Gets the execution priority level for the test.
+ ///
public Priority Priority { get; }
+
+ ///
public int Order => 0;
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The execution priority level. Defaults to .
public ExecutionPriorityAttribute(Priority priority = Priority.Normal)
{
Priority = priority;
diff --git a/TUnit.Core/Attributes/TestMetadata/ExplicitAttribute.cs b/TUnit.Core/Attributes/TestMetadata/ExplicitAttribute.cs
index 22224c060e..afc20b9528 100644
--- a/TUnit.Core/Attributes/TestMetadata/ExplicitAttribute.cs
+++ b/TUnit.Core/Attributes/TestMetadata/ExplicitAttribute.cs
@@ -2,11 +2,33 @@
namespace TUnit.Core;
+///
+/// Marks a test method or class to only run when explicitly selected.
+/// Tests marked with this attribute will not run as part of normal test execution
+/// and must be targeted directly by name or filter.
+///
+///
+/// This is useful for long-running tests, tests that require specific environments,
+/// or tests that should only be run manually by a developer.
+///
+///
+///
+/// [Test]
+/// [Explicit]
+/// public void LongRunningPerformanceTest()
+/// {
+/// // Only runs when explicitly selected
+/// }
+///
+///
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class)]
public sealed class ExplicitAttribute(
[CallerFilePath] string callerFile = "",
[CallerMemberName] string callerMemberName = "")
: TUnitAttribute
{
+ ///
+ /// Gets a string identifying where this attribute was applied (file path and member name).
+ ///
public string For { get; } = $"{callerFile} {callerMemberName}".Trim();
}
diff --git a/TUnit.Core/HookType.cs b/TUnit.Core/HookType.cs
index 14e90f29d3..db3293c6ab 100644
--- a/TUnit.Core/HookType.cs
+++ b/TUnit.Core/HookType.cs
@@ -1,5 +1,9 @@
namespace TUnit.Core;
+///
+/// Specifies the scope at which a hook (, ,
+/// , ) runs.
+///
public enum HookType
{
///
diff --git a/TUnit.Core/TestContext.cs b/TUnit.Core/TestContext.cs
index 37641c0f38..50cd62cadb 100644
--- a/TUnit.Core/TestContext.cs
+++ b/TUnit.Core/TestContext.cs
@@ -11,8 +11,22 @@
namespace TUnit.Core;
///
-/// Simplified test context for the new architecture
+/// Provides access to the current test's metadata, execution state, output, and configuration.
+/// Use to access the context of the currently executing test.
///
+///
+///
+/// TestContext exposes its functionality through organized interface properties:
+/// for test lifecycle and result management,
+/// for capturing output and attaching artifacts,
+/// for test identity and details,
+/// for parallel execution control,
+/// for test dependency information,
+/// for storing custom state during test execution,
+/// for subscribing to test lifecycle events,
+/// and for creating unique resource names.
+///
+///
[DebuggerDisplay("{TestDetails.ClassType.Name}.{GetDisplayName(),nq}")]
public partial class TestContext : Context,
ITestExecution, ITestParallelization, ITestOutput, ITestMetadata, ITestDependencies, ITestStateBag, ITestEvents, ITestIsolation
@@ -35,16 +49,49 @@ public TestContext(string testName, IServiceProvider serviceProvider, ClassHookC
_testContextsById[Id] = this;
}
+ ///
+ /// Gets the unique identifier for this test instance.
+ ///
public string Id { get; }
- // Zero-allocation interface properties for organized API access
+ ///
+ /// Gets access to test execution state, result management, cancellation, and retry information.
+ ///
public ITestExecution Execution => this;
+
+ ///
+ /// Gets access to parallel execution control and priority configuration.
+ ///
public ITestParallelization Parallelism => this;
+
+ ///
+ /// Gets access to test output capture, timing, and artifact management.
+ ///
public ITestOutput Output => this;
+
+ ///
+ /// Gets access to test identity, details, and display name configuration.
+ ///
public ITestMetadata Metadata => this;
+
+ ///
+ /// Gets access to test dependency information and relationship queries.
+ ///
public ITestDependencies Dependencies => this;
+
+ ///
+ /// Gets access to a thread-safe bag for storing custom state during test execution.
+ ///
public ITestStateBag StateBag => this;
+
+ ///
+ /// Gets access to test lifecycle events (registered, start, end, skip, retry, dispose).
+ ///
public ITestEvents Events => this;
+
+ ///
+ /// Gets access to helpers for creating isolated resource names unique to this test instance.
+ ///
public ITestIsolation Isolation => this;
internal IServiceProvider Services => ServiceProvider;
@@ -61,6 +108,20 @@ public TestContext(string testName, IServiceProvider serviceProvider, ClassHookC
private string? _buildTimeOutput;
private string? _buildTimeErrorOutput;
+ ///
+ /// Gets the for the currently executing test, or null if no test is running.
+ /// This is an async-local property that is automatically set by the test engine.
+ ///
+ ///
+ ///
+ /// [Test]
+ /// public void MyTest()
+ /// {
+ /// var context = TestContext.Current!;
+ /// context.Output.WriteLine("Running test: " + context.Metadata.TestName);
+ /// }
+ ///
+ ///
public static new TestContext? Current
{
get => TestContexts.Value;
@@ -71,8 +132,16 @@ internal set
}
}
+ ///
+ /// Gets a by its unique identifier, or null if not found.
+ ///
+ /// The unique identifier of the test context.
+ /// The matching , or null.
public static TestContext? GetById(string id) => _testContextsById.GetValueOrDefault(id);
+ ///
+ /// Gets the dictionary of test parameters indexed by parameter name.
+ ///
public static IReadOnlyDictionary> Parameters => InternalParametersDictionary;
private static IConfiguration? _configuration;
@@ -91,6 +160,10 @@ public static IConfiguration Configuration
internal set => _configuration = value;
}
+ ///
+ /// Gets the output directory of the test assembly, or null if it cannot be determined.
+ /// This is typically the directory where the test binaries are located.
+ ///
public static string? OutputDirectory
{
get
@@ -112,6 +185,9 @@ string GetOutputDirectory()
}
}
+ ///
+ /// Gets or sets the current working directory for the test process.
+ ///
public static string WorkingDirectory
{
get => Environment.CurrentDirectory;
@@ -147,7 +223,7 @@ internal void SetDataSourceDisplayName(string displayName)
///
- /// Will be null until initialized by TestOrchestrator
+ /// Gets the class-level hook context for this test, providing access to class-scoped hooks and state.
///
public ClassHookContext ClassContext { get; }
@@ -176,6 +252,9 @@ internal override void SetAsyncLocalContext()
///
internal bool IsDiscoveryInstanceReused { get; set; }
+ ///
+ /// Gets a synchronization object that can be used for thread-safe operations within this test context.
+ ///
public object Lock { get; } = new();
diff --git a/TUnit.Core/TestDetails.cs b/TUnit.Core/TestDetails.cs
index 4227e5b81c..e8869869e3 100644
--- a/TUnit.Core/TestDetails.cs
+++ b/TUnit.Core/TestDetails.cs
@@ -5,7 +5,9 @@
namespace TUnit.Core;
///
-/// Simplified test details for the new architecture
+/// Contains detailed metadata about a test, including its identity, class type, method information,
+/// arguments, timeout, retry settings, categories, and custom properties.
+/// Access via and its property.
///
public partial class TestDetails : ITestIdentity, ITestClass, ITestMethod, ITestConfiguration, ITestLocation, ITestDetailsMetadata
{
diff --git a/TUnit.Core/TestResult.cs b/TUnit.Core/TestResult.cs
index c37fe74e38..0b16ffc349 100644
--- a/TUnit.Core/TestResult.cs
+++ b/TUnit.Core/TestResult.cs
@@ -2,23 +2,45 @@
namespace TUnit.Core;
+///
+/// Represents the outcome of a test execution, including the state, timing, exception information, and output.
+/// Access via on .
+///
public record TestResult
{
///
- /// Only final states should be used.
+ /// Gets the final state of the test (Passed, Failed, Skipped, Timeout, or Cancelled).
///
public required TestState State { get; init; }
+ ///
+ /// Gets the timestamp when test execution started, or null if the test did not start.
+ ///
public required DateTimeOffset? Start { get; init; }
+ ///
+ /// Gets the timestamp when test execution ended, or null if the test did not complete.
+ ///
public required DateTimeOffset? End { get; init; }
+ ///
+ /// Gets the duration of test execution, or null if the test did not complete.
+ ///
public required TimeSpan? Duration { get; init; }
+ ///
+ /// Gets the exception that caused the test to fail, or null if the test passed or was skipped.
+ ///
public required Exception? Exception { get; init; }
+ ///
+ /// Gets the name of the computer where the test was executed.
+ ///
public required string ComputerName { get; init; }
+ ///
+ /// Gets the captured standard output from the test execution.
+ ///
public string? Output { get; internal set; }
[JsonIgnore]
diff --git a/TUnit.Core/TestState.cs b/TUnit.Core/TestState.cs
index 54b6604ad0..21dd64e835 100644
--- a/TUnit.Core/TestState.cs
+++ b/TUnit.Core/TestState.cs
@@ -1,14 +1,52 @@
namespace TUnit.Core;
+///
+/// Represents the possible states of a test during its lifecycle.
+///
public enum TestState
{
+ ///
+ /// The test has not yet started execution.
+ ///
NotStarted,
+
+ ///
+ /// The test is waiting for its dependencies to complete before it can execute.
+ ///
WaitingForDependencies,
+
+ ///
+ /// The test is queued and waiting for an available execution slot.
+ ///
Queued,
+
+ ///
+ /// The test is currently executing.
+ ///
Running,
+
+ ///
+ /// The test completed successfully.
+ ///
Passed,
+
+ ///
+ /// The test failed due to an assertion failure or unhandled exception.
+ ///
Failed,
+
+ ///
+ /// The test was skipped and did not execute.
+ ///
Skipped,
+
+ ///
+ /// The test exceeded its configured timeout duration.
+ ///
Timeout,
+
+ ///
+ /// The test was cancelled before completion.
+ ///
Cancelled
}