From 9af1bd7e56e457de6d319660e362728b8bb73873 Mon Sep 17 00:00:00 2001 From: David Driscoll Date: Sun, 28 Jun 2026 19:42:09 -0400 Subject: [PATCH] Refactor TestRecord class and add interop features for test execution context --- Directory.Packages.props | 1 + ...et.Surgery.Extensions.Testing.TUnit.csproj | 4 + src/Testing.TUnit/TestRecord.Interop.cs | 411 ++++++++++++++++++ src/Testing.TUnit/TestRecord.cs | 41 +- ...t.Surgery.Extensions.Testing.XUnit3.csproj | 4 + src/Testing.XUnit3/XUnitTestContext.cs | 100 +---- src/Testing.XUnit3/template.scriban | 74 ++++ .../Generators/GeneratorContextTests.cs | 2 +- test/Testing.TUnit.Tests/LoggerTestTests.cs | 2 +- 9 files changed, 518 insertions(+), 121 deletions(-) create mode 100644 src/Testing.TUnit/TestRecord.Interop.cs create mode 100644 src/Testing.XUnit3/template.scriban diff --git a/Directory.Packages.props b/Directory.Packages.props index e25731048c..f640b4fca4 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -18,6 +18,7 @@ + diff --git a/src/Testing.TUnit/Rocket.Surgery.Extensions.Testing.TUnit.csproj b/src/Testing.TUnit/Rocket.Surgery.Extensions.Testing.TUnit.csproj index 3841c71276..9725524069 100644 --- a/src/Testing.TUnit/Rocket.Surgery.Extensions.Testing.TUnit.csproj +++ b/src/Testing.TUnit/Rocket.Surgery.Extensions.Testing.TUnit.csproj @@ -6,9 +6,13 @@ true + + + + diff --git a/src/Testing.TUnit/TestRecord.Interop.cs b/src/Testing.TUnit/TestRecord.Interop.cs new file mode 100644 index 0000000000..8a93652b4f --- /dev/null +++ b/src/Testing.TUnit/TestRecord.Interop.cs @@ -0,0 +1,411 @@ +using TUnit.Core.Enums; +using TUnit.Core.Interfaces; + +namespace Rocket.Surgery.Extensions.Testing; + +public partial class TestRecord +{ + /// + /// Gets the current phase of test execution (Discovery, Execution, Cleanup, etc.). + /// + public TestPhase Phase => ( (ITestExecution)TestContext ).Phase; + + /// + /// Gets the test result after execution completes, or null if the test is still running. + /// + public TestResult? Result => ( (ITestExecution)TestContext ).Result; + + /// + /// Gets the cancellation token for this test execution. + /// Check to honor cancellation requests. + /// + public CancellationToken CancellationToken => ( (ITestExecution)TestContext ).CancellationToken; + + /// + /// Gets the timestamp when test execution started, or null if not yet started. + /// + public DateTimeOffset? TestStart => ( (ITestExecution)TestContext ).TestStart; + + /// + /// Gets the timestamp when test execution ended, or null if not yet completed. + /// + public DateTimeOffset? TestEnd => ( (ITestExecution)TestContext ).TestEnd; + + /// + /// Gets the current retry attempt number (0 for first attempt, 1+ for retries). + /// + public int CurrentRetryAttempt => ( (ITestExecution)TestContext ).CurrentRetryAttempt; + + /// + /// Gets the results of prior execution attempts that triggered a retry, in attempt order. + /// Empty when the test was not retried. The final (surviving) attempt is reflected by + /// and is not included here — so for a test that ran 3 times, + /// RetryAttempts.Count is 2 (the two failed prior attempts) and + /// is 2 (the zero-based index of the surviving attempt). + /// Each entry is the that attempt produced before it was retried. + /// + /// + /// This member has no default implementation because ITestExecution targets + /// netstandard2.0, which predates default interface members. External types that implement + /// ITestExecution directly must add this property when upgrading — return an empty + /// list (e.g. Array.Empty<TestResult>()) if retry history is not tracked. + /// + public IReadOnlyList RetryAttempts => ( (ITestExecution)TestContext ).RetryAttempts; + + /// + /// Gets the reason why this test was skipped, or null if not skipped. + /// + public string? SkipReason => ( (ITestExecution)TestContext ).SkipReason; + + /// + /// Gets the retry function that determines whether a failed test should be retried. + /// + public Func>? RetryFunc => ( (ITestExecution)TestContext ).RetryFunc; + + /// + /// Overrides the test result with a specific state and custom reason. + /// + /// The desired test state (Passed, Failed, Skipped, Timeout, or Cancelled) + /// The reason for overriding the result (cannot be empty) + /// Thrown when reason is empty, whitespace, or state is invalid (NotStarted, WaitingForDependencies, Queued, Running) + /// Thrown when result has already been overridden + /// + /// This method can only be called once per test. Subsequent calls will throw an exception. + /// Only final states are allowed: Passed, Failed, Skipped, Timeout, or Cancelled. Intermediate states like Running, Queued, NotStarted, or WaitingForDependencies are rejected. + /// The original exception (if any) is preserved in . + /// When overriding to Failed, the original exception is retained in . + /// When overriding to Passed or Skipped, the Exception property is cleared but preserved in OriginalException. + /// Best practice: Call this from or After(Test) hooks. + /// + /// + /// + /// // Override failed test to passed + /// public class RetryOnInfrastructureErrorAttribute : Attribute, ITestEndEventReceiver + /// { + /// public ValueTask OnTestEnd(TestContext context) + /// { + /// if (context.Result?.Exception is HttpRequestException) + /// { + /// context.Execution.OverrideResult(TestState.Passed, "Infrastructure error - not a test failure"); + /// } + /// return default; + /// } + /// public int Order => 0; + /// } + /// + /// // Override failed test to skipped + /// public class IgnoreOnWeekendAttribute : Attribute, ITestEndEventReceiver + /// { + /// public ValueTask OnTestEnd(TestContext context) + /// { + /// if (context.Result?.State == TestState.Failed && DateTime.Now.DayOfWeek is DayOfWeek.Saturday or DayOfWeek.Sunday) + /// { + /// context.Execution.OverrideResult(TestState.Skipped, "Failures ignored on weekends"); + /// } + /// return default; + /// } + /// public int Order => 0; + /// } + /// + /// + public void OverrideResult(TestState state, string reason) => ( (ITestExecution)TestContext ).OverrideResult(state, reason); + + /// + /// Gets or sets a custom hook executor that overrides the default execution behavior for test-level hooks. + /// Allows wrapping hook execution in custom logic (e.g., running on a specific thread). + /// + public IHookExecutor? CustomHookExecutor { get => ( (ITestExecution)TestContext ).CustomHookExecutor; set => ( (ITestExecution)TestContext ).CustomHookExecutor = value; } + + /// + /// Gets or sets whether the test result should be reported to test runners. + /// Defaults to true. Set to false to suppress reporting for internal or diagnostic tests. + /// + public bool ReportResult { get => ( (ITestExecution)TestContext ).ReportResult; set => ( (ITestExecution)TestContext ).ReportResult = value; } + + /// + /// Gets or sets whether the test should be hidden from test discovery/explorer. + /// Defaults to false. Set to true to hide the test from discovery notifications + /// while still allowing it to execute when run directly. + /// + public bool IsNotDiscoverable { get => ( (ITestExecution)TestContext ).IsNotDiscoverable; set => ( (ITestExecution)TestContext ).IsNotDiscoverable = value; } + + /// + /// Links an external cancellation token to this test's execution token. + /// Useful for coordinating cancellation across multiple operations or tests. + /// + /// The external cancellation token to link + public void AddLinkedCancellationToken(CancellationToken cancellationToken) => ( (ITestExecution)TestContext ).AddLinkedCancellationToken(cancellationToken); + + /// + /// Gets or sets the execution priority for this test. + /// Higher priority tests may execute before lower priority tests when resources are limited. + /// + public Priority ExecutionPriority { get => ( (ITestParallelization)TestContext ).ExecutionPriority; set => ( (ITestParallelization)TestContext ).ExecutionPriority = value; } + + /// + /// Gets the parallel limiter that controls how many tests can run concurrently. + /// + public IParallelLimit? Limiter => ( (ITestParallelization)TestContext ).Limiter; + + /// + /// Adds a parallel constraint to this test context. + /// Multiple constraints can be combined to create complex parallelization rules. + /// + /// The constraint to add + public void AddConstraint(IParallelConstraint constraint) => ( (ITestParallelization)TestContext ).AddConstraint(constraint); + + /// + /// Gets the text writer for standard output. + /// Use this for writing test progress, debugging information, or general output. + /// Thread-safe for concurrent writes. + /// + public TextWriter StandardOutput => ( (ITestOutput)TestContext ).StandardOutput; + + /// + /// Gets the text writer for error output. + /// Use this for writing error messages, warnings, or diagnostic information. + /// Thread-safe for concurrent writes. + /// + public TextWriter ErrorOutput => ( (ITestOutput)TestContext ).ErrorOutput; + + /// + /// Gets the collection of artifacts (files, screenshots, logs) attached to this test. + /// Artifacts are preserved after test execution for review and debugging. + /// + public IReadOnlyCollection Artifacts => ( (ITestOutput)TestContext ).Artifacts; + + /// + /// Attaches an artifact (file, screenshot, log, etc.) to this test. + /// Artifacts are preserved after test execution. + /// Thread-safe for concurrent calls. + /// + /// The artifact to attach + public void AttachArtifact(Artifact artifact) => ( (ITestOutput)TestContext ).AttachArtifact(artifact); + + /// + /// Attaches a file as an artifact to this test. + /// Artifacts are preserved after test execution. + /// Thread-safe for concurrent calls. + /// + /// The path to the file to attach + /// Optional display name for the artifact. Defaults to the file name. + /// Optional description of the artifact + public void AttachArtifact(string filePath, string? displayName = null, string? description = null) => ( (ITestOutput)TestContext ).AttachArtifact(filePath, displayName, description); + + /// + /// Gets all standard output written during test execution as a single string. + /// + /// The accumulated standard output + public string GetStandardOutput() => ( (ITestOutput)TestContext ).GetStandardOutput(); + + /// + /// Gets all error output written during test execution as a single string. + /// + /// The accumulated error output + public string GetErrorOutput() => ( (ITestOutput)TestContext ).GetErrorOutput(); + + /// + /// Writes a line of text to standard output. + /// Convenience method for StandardOutput.WriteLine(message). + /// Thread-safe for concurrent calls. + /// + /// The message to write + public void WriteLine(string message) => StandardOutput.WriteLine(message); + + /// + /// Writes a line of text to error output. + /// Convenience method for ErrorOutput.WriteLine(message). + /// Thread-safe for concurrent calls. + /// + /// The error message to write + public void WriteError(string message) => ErrorOutput.WriteLine(message); + + /// + /// Gets the unique identifier for the test definition (template/source) that generated this test. + /// This ID is shared across all instances of parameterized tests. + /// + public string DefinitionId => ( (ITestMetadata)TestContext ).DefinitionId; + + /// + /// Gets the detailed metadata about this test, including class type, method info, and arguments. + /// + public TestDetails TestDetails => ( (ITestMetadata)TestContext ).TestDetails; + + /// + /// Gets the base name of the test method. + /// + public string TestName => ( (ITestMetadata)TestContext ).TestName; + + /// + /// Gets or sets the display name for the test. + /// When reading, returns the custom display name if set, otherwise computes from test name and arguments. + /// Setting this value overrides the default generated name. + /// + public string DisplayName { get => ( (ITestMetadata)TestContext ).DisplayName; set => ( (ITestMetadata)TestContext ).DisplayName = value; } + + /// + /// Gets or sets the custom display name formatter type used to format test names. + /// Must implement IDisplayNameFormatter interface. + /// + public Type? DisplayNameFormatter { get => ( (ITestMetadata)TestContext ).DisplayNameFormatter; set => ( (ITestMetadata)TestContext ).DisplayNameFormatter = value; } + + /// + /// Gets the collection of tests that this test depends on. + /// Tests in this collection will execute before this test runs. + /// + public IReadOnlyList DependsOn => ( (ITestDependencies)TestContext ).DependsOn; + + /// + /// Gets the parent test ID if this test is part of a relationship. + /// + public string? ParentTestId => ( (ITestDependencies)TestContext ).ParentTestId; + + /// + /// Gets the relationship type of this test. + /// + public TestRelationship Relationship => ( (ITestDependencies)TestContext ).Relationship; + + /// + /// Gets all registered tests that match the specified predicate. + /// + /// The predicate to filter tests by. + /// A read-only list of matching test contexts. + public IReadOnlyList GetTests(Func predicate) => ( (ITestDependencies)TestContext ).GetTests(predicate); + + /// + /// Gets all registered tests that match the specified test name. + /// + /// The name of the test method. + /// A read-only list of matching test contexts. + public IReadOnlyList GetTests(string testName) => ( (ITestDependencies)TestContext ).GetTests(testName); + + /// + /// Gets all registered tests that match the specified test name and class type. + /// + /// The name of the test method. + /// The type of the test class. + /// A read-only list of matching test contexts. + public IReadOnlyList GetTests(string testName, Type classType) => ( (ITestDependencies)TestContext ).GetTests(testName, classType); + + /// + /// Gets the underlying concurrent dictionary for direct access. + /// + public ConcurrentDictionary Items => ( (ITestStateBag)TestContext ).Items; + + /// + /// Gets or sets a value in the state bag. + /// + /// The key of the value to get or set. + /// The value associated with the specified key. + public object? this[string key] => ( (ITestStateBag)TestContext )[key]; + + /// + /// Gets the number of items in the state bag. + /// + public int Count => ( (ITestStateBag)TestContext ).Count; + + /// + /// Gets a value indicating whether the specified key exists in the state bag. + /// + /// The key to check. + /// true if the key exists; otherwise, false. + public bool ContainsKey(string key) => ( (ITestStateBag)TestContext ).ContainsKey(key); + + /// + /// Gets the value associated with the specified key, or adds it if it does not exist. + /// + /// The type of the value. + /// The key of the value to get or add. + /// The function used to generate a value for the key if it does not exist. + /// The value for the key. This will be either the existing value for the key if the key is already in the dictionary, or the new value if the key was not in the dictionary. + /// Thrown if a value already exists for the key but is not of type . + public T GetOrAdd(string key, Func valueFactory) => ( (ITestStateBag)TestContext ).GetOrAdd(key, valueFactory); + + /// + /// Attempts to get the value associated with the specified key. + /// + /// The type of the value. + /// The key of the value to get. + /// When this method returns, contains the value associated with the specified key, if the key is found and the value is of the correct type; otherwise, the default value for the type of the value parameter. + /// true if the key was found and the value is of the correct type; otherwise, false. + public bool TryGetValue(string key, [MaybeNullWhen(false)] out T value) => ( (ITestStateBag)TestContext ).TryGetValue(key, out value); + + /// + /// Attempts to remove a value with the specified key. + /// + /// The key of the element to remove. + /// When this method returns, contains the object removed from the bag, or null if the key does not exist. + /// true if the object was removed successfully; otherwise, false. + public bool TryRemove(string key, [MaybeNullWhen(false)] out object? value) => ( (ITestStateBag)TestContext ).TryRemove(key, out value); + + /// + /// Gets the event that is raised when the test context is disposed. + /// + public AsyncEvent? OnDispose => ( (ITestEvents)TestContext ).OnDispose; + + /// + /// Gets the event that is raised when the test has been registered with the test runner. + /// + public AsyncEvent? OnTestRegistered => ( (ITestEvents)TestContext ).OnTestRegistered; + + /// + /// Gets the event that is raised before the test is initialized. + /// + public AsyncEvent? OnInitialize => ( (ITestEvents)TestContext ).OnInitialize; + + /// + /// Gets the event that is raised before the test method is invoked. + /// + public AsyncEvent? OnTestStart => ( (ITestEvents)TestContext ).OnTestStart; + + /// + /// Gets the event that is raised after the test method has completed. + /// + public AsyncEvent? OnTestEnd => ( (ITestEvents)TestContext ).OnTestEnd; + + /// + /// Gets the event that is raised if the test was skipped. + /// + public AsyncEvent? OnTestSkipped => ( (ITestEvents)TestContext ).OnTestSkipped; + + /// + /// Gets the event that is raised before a test is retried. + /// + public AsyncEvent<(TestContext TestContext, int RetryAttempt)>? OnTestRetry => ( (ITestEvents)TestContext ).OnTestRetry; + + /// + /// Gets a unique identifier for this test instance. + /// This value is assigned atomically and is guaranteed to be unique across all test instances in the process. + /// + public int UniqueId => ( (ITestIsolation)TestContext ).UniqueId; + + /// + /// Creates an isolated name by combining a base name with the test's unique identifier. + /// Use for database tables, Redis keys, Kafka topics, etc. + /// + /// The base name for the resource. + /// A unique name in the format "Test_{UniqueId}_{baseName}". + /// + /// + /// // In a test with UniqueId = 42: + /// var tableName = TestContext.Current!.Isolation.GetIsolatedName("todos"); // Returns "Test_42_todos" + /// var topicName = TestContext.Current!.Isolation.GetIsolatedName("orders"); // Returns "Test_42_orders" + /// + /// + public string GetIsolatedName(string baseName) => ( (ITestIsolation)TestContext ).GetIsolatedName(baseName); + + /// + /// Creates an isolated prefix using the test's unique identifier. + /// Use for key prefixes in Redis, Kafka topic prefixes, etc. + /// + /// The separator character. Defaults to "_". + /// A unique prefix in the format "test{separator}{UniqueId}{separator}". + /// + /// + /// // In a test with UniqueId = 42: + /// var prefix = TestContext.Current!.Isolation.GetIsolatedPrefix(); // Returns "test_42_" + /// var dotPrefix = TestContext.Current!.Isolation.GetIsolatedPrefix("."); // Returns "test.42." + /// + /// + public string GetIsolatedPrefix(string separator = "_") => ( (ITestIsolation)TestContext ).GetIsolatedPrefix(separator); +} diff --git a/src/Testing.TUnit/TestRecord.cs b/src/Testing.TUnit/TestRecord.cs index 52ecc80bf3..b874d5cfc1 100644 --- a/src/Testing.TUnit/TestRecord.cs +++ b/src/Testing.TUnit/TestRecord.cs @@ -9,30 +9,32 @@ namespace Rocket.Surgery.Extensions.Testing; /// /// The xunit test context /// -/// /// /// /// [PublicAPI] -public abstract class TUnitTestRecord( - TestContext context, - LogEventLevel logEventLevel = LogEventLevel.Verbose, - string? outputTemplate = null, - Action? configureLogger = null - ) : RocketSurgeryTestContext( - configureLogger, - logEventLevel, - outputTemplate - ) - where TContext : RocketSurgeryTestContext, ILoggingTestContext +public abstract partial class TestRecord + (LogEventLevel logEventLevel = LogEventLevel.Verbose, string? outputTemplate = null, Action? configureLogger = null) + : RocketSurgeryTestContext(configureLogger, logEventLevel, outputTemplate) + where TContext : RocketSurgeryTestContext { - private readonly TestContext _context = context; + /// + /// Represents the current test context for xUnit tests. + /// + /// + /// Provides access to the active test context during the execution of a test. + /// This property is typically used to retrieve contextual information or perform + /// test-specific logging operations. + /// + public TestContext TestContext { get; } = TestContext.Current!; + private readonly LogEventLevel _logEventLevel = logEventLevel; /// - protected override void ConfigureLogger(TContext context, LoggerConfiguration loggerConfiguration) => loggerConfiguration - .MinimumLevel.Is(_logEventLevel) - .WriteTo.Sink(new Sink(_context)); + protected override void ConfigureLogger(TContext context, LoggerConfiguration loggerConfiguration) => + loggerConfiguration + .MinimumLevel.Is(_logEventLevel) + .WriteTo.Sink(new Sink(TestContext)); } /// @@ -41,22 +43,19 @@ protected override void ConfigureLogger(TContext context, LoggerConfiguration lo [PublicAPI] public class TestRecord ( - TestContext context, LogEventLevel logEventLevel = LogEventLevel.Verbose, string? outputTemplate = null, Action? configureLogger = null) - : TUnitTestRecord(context, logEventLevel, outputTemplate, configureLogger) + : TestRecord(logEventLevel, outputTemplate, configureLogger) { /// /// Create the test record /// - /// /// /// /// public static TestRecord Create( - TestContext testContext, LogEventLevel logEventLevel = LogEventLevel.Verbose, string? outputTemplate = null - ) => new(testContext, logEventLevel, outputTemplate); + ) => new(logEventLevel, outputTemplate); } diff --git a/src/Testing.XUnit3/Rocket.Surgery.Extensions.Testing.XUnit3.csproj b/src/Testing.XUnit3/Rocket.Surgery.Extensions.Testing.XUnit3.csproj index ec804942d1..6ce3994db5 100644 --- a/src/Testing.XUnit3/Rocket.Surgery.Extensions.Testing.XUnit3.csproj +++ b/src/Testing.XUnit3/Rocket.Surgery.Extensions.Testing.XUnit3.csproj @@ -6,9 +6,13 @@ true + + + + diff --git a/src/Testing.XUnit3/XUnitTestContext.cs b/src/Testing.XUnit3/XUnitTestContext.cs index facf2af94b..18d8b9ad2d 100644 --- a/src/Testing.XUnit3/XUnitTestContext.cs +++ b/src/Testing.XUnit3/XUnitTestContext.cs @@ -1,7 +1,6 @@ using Serilog; using Serilog.Events; using Xunit; -using Xunit.Sdk; namespace Rocket.Surgery.Extensions.Testing; @@ -16,7 +15,7 @@ namespace Rocket.Surgery.Extensions.Testing; /// /// [PublicAPI] -public abstract class XUnitTestContext( +public abstract partial class XUnitTestContext( ITestContextAccessor testContextAccessor, LogEventLevel logEventLevel = LogEventLevel.Verbose, string? outputTemplate = null, @@ -25,109 +24,14 @@ public abstract class XUnitTestContext( where TContext : RocketSurgeryTestContext, ILoggingTestContext, ITestContext { private readonly LogEventLevel _logEventLevel = logEventLevel; + [BeaKona.AutoInterface(TemplateFileName = "template.scriban")] private readonly ITestContext _testContext = testContextAccessor.Current; - private ConcurrentDictionary _keyValueStorage; /// protected override void ConfigureLogger(TContext context, LoggerConfiguration loggerConfiguration) => loggerConfiguration .MinimumLevel.Is(_logEventLevel) .WriteTo.Sink(new XUnitSink(context)); - /// - public void AddAttachment(string name, string value) => _testContext.AddAttachment(name, value); - - /// - public void AddAttachment(string name, string value, bool replaceExistingValue) => _testContext.AddAttachment(name, value, replaceExistingValue); - - /// - public void AddAttachment(string name, byte[] value, string mediaType = "application/octet-stream") => _testContext.AddAttachment(name, value, mediaType); - - /// - public void AddAttachment(string name, byte[] value, bool replaceExistingValue, string mediaType = "application/octet-stream") => throw new NotImplementedException(); - - /// - public void AddWarning(string message) => _testContext.AddWarning(message); - - /// - public void CancelCurrentTest() => _testContext.CancelCurrentTest(); - - /// - public ValueTask GetFixture(Type fixtureType) => _testContext.GetFixture(fixtureType); - - /// - public void SendDiagnosticMessage(string message) => _testContext.SendDiagnosticMessage(message); - - /// - public void SendDiagnosticMessage(string format, object? arg0) => _testContext.SendDiagnosticMessage(format, arg0); - - /// - public void SendDiagnosticMessage(string format, object? arg0, object? arg1) => _testContext.SendDiagnosticMessage(format, arg0, arg1); - - /// - public void SendDiagnosticMessage(string format, object? arg0, object? arg1, object? arg2) => _testContext.SendDiagnosticMessage(format, arg0, arg1, arg2); - - /// - public void SendDiagnosticMessage(string format, params object?[] args) => _testContext.SendDiagnosticMessage(format, args); - - /// - public IReadOnlyDictionary? Attachments => _testContext.Attachments; - - /// - public CancellationToken CancellationToken => _testContext.CancellationToken; - - /// - ConcurrentDictionary ITestContext.KeyValueStorage => _keyValueStorage; - - /// - public TestPipelineStage PipelineStage => _testContext.PipelineStage; - - /// - public ITest? Test => _testContext.Test; - - /// - public ITestAssembly? TestAssembly => _testContext.TestAssembly; - - /// - public TestEngineStatus? TestAssemblyStatus => _testContext.TestAssemblyStatus; - - /// - public ITestCase? TestCase => _testContext.TestCase; - - /// - public TestEngineStatus? TestCaseStatus => _testContext.TestCaseStatus; - - /// - public ITestClass? TestClass => _testContext.TestClass; - - /// - public object? TestClassInstance => _testContext.TestClassInstance; - - /// - public TestEngineStatus? TestClassStatus => _testContext.TestClassStatus; - - /// - public ITestCollection? TestCollection => _testContext.TestCollection; - - /// - public TestEngineStatus? TestCollectionStatus => _testContext.TestCollectionStatus; - - /// - public ITestMethod? TestMethod => _testContext.TestMethod; - - /// - public TestEngineStatus? TestMethodStatus => _testContext.TestMethodStatus; - - /// - public ITestOutputHelper? TestOutputHelper => _testContext.TestOutputHelper; - - /// - public TestResultState? TestState => _testContext.TestState; - - /// - public TestEngineStatus? TestStatus => _testContext.TestStatus; - - /// - public IReadOnlyList? Warnings => _testContext.Warnings; } /// diff --git a/src/Testing.XUnit3/template.scriban b/src/Testing.XUnit3/template.scriban new file mode 100644 index 0000000000..efc876ceca --- /dev/null +++ b/src/Testing.XUnit3/template.scriban @@ -0,0 +1,74 @@ +{{~for method in methods~}} +/// +public {{method.is_async?"async ":""}}{{method.return_type}} {{method.name}}({{method.arguments_definition}}) +{ +{{~if method.is_async~}} + {{~for reference in references~}} + var temp{{for.index}} = (({{interface}})this.{{reference}}).{{method.name}}({{method.call_arguments}}).ConfigureAwait(false); + {{~end~}} + {{~for reference in references~}} + {{for.last && method.return_expected ? "return " : ""}}await temp{{for.index}}; + {{~end~}} +{{~else~}} + {{~for reference in references~}} + {{for.last && method.return_expected ? "return " : ""}}(({{interface}})this.{{reference}}).{{method.name}}({{method.call_arguments}}); + {{~end~}} +{{~end~}} +} + +{{~end~}} +{{~for property in properties~}} +/// +public {{property.type}} {{property.name}} +{ +{{~if property.have_getter~}} + get + { + return (({{interface}})this.{{references[0]}}).{{property.name}}; + } +{{~end~}} +{{~if property.have_setter~}} + set + { + } +{{~end~}} +} + +{{~end~}} +{{~for indexer in indexers~}} +/// +public {{indexer.type}} {{indexer.name}}[{{indexer.parameters_definition}}] +{ +{{~if indexer.have_getter~}} + get + { + return (({{interface}})this.{{references[0]}})[{{indexer.call_parameters}}]; + } +{{~end~}} +{{~if indexer.have_setter~}} + set + { + } +{{~end~}} +} + +{{~end~}} +{{~for event in events~}} +/// +public event {{event.type}} {{event.name}} +{ + add + { + {{~for reference in references~}} + (({{interface}})this.{{reference}}).{{event.name}} += value; + {{~end~}} + } + remove + { + {{~for reference in references~}} + (({{interface}})this.{{reference}}).{{event.name}} -= value; + {{~end~}} + } +} + +{{~end~}} diff --git a/test/Testing.TUnit.Tests/Generators/GeneratorContextTests.cs b/test/Testing.TUnit.Tests/Generators/GeneratorContextTests.cs index 5f7003a50b..8a6708b297 100644 --- a/test/Testing.TUnit.Tests/Generators/GeneratorContextTests.cs +++ b/test/Testing.TUnit.Tests/Generators/GeneratorContextTests.cs @@ -8,7 +8,7 @@ namespace Rocket.Surgery.Extensions.Testing.TUnit.Tests.Generators; -public class GeneratorContextTests() : LoggerTest(TestRecord.Create(global::TUnit.Core.TestContext.Current!)) +public class GeneratorContextTests() : LoggerTest(TestRecord.Create()) { [Test] public async Task Should_Build_A_Context() diff --git a/test/Testing.TUnit.Tests/LoggerTestTests.cs b/test/Testing.TUnit.Tests/LoggerTestTests.cs index 6e51de7962..6687cc63cc 100644 --- a/test/Testing.TUnit.Tests/LoggerTestTests.cs +++ b/test/Testing.TUnit.Tests/LoggerTestTests.cs @@ -3,7 +3,7 @@ namespace Rocket.Surgery.Extensions.Testing.TUnit.Tests; -public class LoggerTestTests() : LoggerTest(TestRecord.Create(global::TUnit.Core.TestContext.Current!)) +public class LoggerTestTests() : LoggerTest(TestRecord.Create()) { [Test] public Task Should_Create_A_Log_Stream()