From 14c788eb3b1d7afb4a9bf7f3468e8cd6ab2de40b Mon Sep 17 00:00:00 2001 From: Tom Longhurst <30480171+thomhurst@users.noreply.github.com> Date: Sun, 9 Aug 2026 15:08:35 +0100 Subject: [PATCH] feat: expose results directory --- docs/docs/writing-tests/artifacts.md | 17 +++++++++++++++++ docs/docs/writing-tests/test-context.md | 4 ++++ src/TUnit.Core/TestContext.cs | 17 +++++++++++++++++ .../Framework/TUnitServiceProvider.cs | 2 ++ ...y_Has_No_API_Changes.DotNet10_0.verified.txt | 1 + ...ry_Has_No_API_Changes.DotNet8_0.verified.txt | 1 + ...ry_Has_No_API_Changes.DotNet9_0.verified.txt | 1 + ...brary_Has_No_API_Changes.Net4_7.verified.txt | 1 + tests/TUnit.TestProject/TestContextTests.cs | 11 +++++++++++ 9 files changed, 55 insertions(+) diff --git a/docs/docs/writing-tests/artifacts.md b/docs/docs/writing-tests/artifacts.md index 0189678c7eb..ebc7595e423 100644 --- a/docs/docs/writing-tests/artifacts.md +++ b/docs/docs/writing-tests/artifacts.md @@ -10,6 +10,23 @@ TUnit supports attaching artifacts at two levels: Attach files to individual tests using `TestContext.Current.Output.AttachArtifact()`. +### Writing Artifacts to the Results Directory + +Use `TestContext.ResultsDirectory` to place generated files alongside reports and other +artifacts. The property returns the absolute directory selected by Microsoft.Testing.Platform, +including any `--results-directory` override. + +```csharp +[Test] +public async Task CaptureLog() +{ + var artifactPath = Path.Combine(TestContext.ResultsDirectory, "application.log"); + + await File.WriteAllTextAsync(artifactPath, "Diagnostic information"); + TestContext.Current!.Output.AttachArtifact(artifactPath); +} +``` + ### Basic Usage The simplest way to attach an artifact is by providing just the file path: diff --git a/docs/docs/writing-tests/test-context.md b/docs/docs/writing-tests/test-context.md index d59604a00c6..8c4786f6b5d 100644 --- a/docs/docs/writing-tests/test-context.md +++ b/docs/docs/writing-tests/test-context.md @@ -53,6 +53,10 @@ Both `Output.WriteLine()` and `OutputWriter.WriteLine()` are valid - the `Output Artifacts are particularly useful for debugging test failures, especially in integration tests. You can attach screenshots, logs, videos, configuration files, or any other files that help diagnose issues. +Use `TestContext.ResultsDirectory` to get the absolute Microsoft.Testing.Platform results +directory. It respects configuration such as `--results-directory` and is suitable for files +that should live alongside test reports. + For complete information about working with test artifacts, including session-level artifacts, best practices, and common use cases, see the [Test Artifacts](./artifacts.md) guide. ## Test Isolation diff --git a/src/TUnit.Core/TestContext.cs b/src/TUnit.Core/TestContext.cs index 7bb54f23eda..7fb2c869616 100644 --- a/src/TUnit.Core/TestContext.cs +++ b/src/TUnit.Core/TestContext.cs @@ -231,6 +231,7 @@ public ContextScope MakeCurrent() public static IReadOnlyDictionary> Parameters => InternalParametersDictionary; private static IConfiguration? _configuration; + private static string? _resultsDirectory; /// /// Gets the test configuration. Throws a descriptive exception if accessed before initialization. @@ -246,6 +247,22 @@ public static IConfiguration Configuration internal set => _configuration = value; } + /// + /// Gets the absolute path to the directory where test results are written. + /// This respects Microsoft.Testing.Platform configuration, including the + /// --results-directory command-line option. + /// + /// Thrown if accessed before the test engine initializes it. + public static string ResultsDirectory + { + get => _resultsDirectory ?? throw new InvalidOperationException( + "TestContext.ResultsDirectory has not been initialized. " + + "This property is only available after the TUnit test engine has started. " + + "If you are accessing this from a static constructor or field initializer, " + + "consider moving the code to a test setup method or test body instead."); + internal set => _resultsDirectory = 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. diff --git a/src/TUnit.Engine/Framework/TUnitServiceProvider.cs b/src/TUnit.Engine/Framework/TUnitServiceProvider.cs index 94edfe38504..94adb348e96 100644 --- a/src/TUnit.Engine/Framework/TUnitServiceProvider.cs +++ b/src/TUnit.Engine/Framework/TUnitServiceProvider.cs @@ -2,6 +2,7 @@ using System.Runtime.CompilerServices; using Microsoft.Testing.Platform.Capabilities.TestFramework; using Microsoft.Testing.Platform.CommandLine; +using Microsoft.Testing.Platform.Configurations; using Microsoft.Testing.Platform.Extensions; using Microsoft.Testing.Platform.Extensions.TestFramework; using Microsoft.Testing.Platform.Logging; @@ -77,6 +78,7 @@ public TUnitServiceProvider(IExtension extension, var configuration = frameworkServiceProvider.GetConfiguration(); TestContext.Configuration = new ConfigurationAdapter(configuration); + TestContext.ResultsDirectory = configuration.GetTestResultDirectory(); // Register capabilities so they're available to test contexts Register(capabilities); diff --git a/tests/TUnit.PublicAPI/Tests.Core_Library_Has_No_API_Changes.DotNet10_0.verified.txt b/tests/TUnit.PublicAPI/Tests.Core_Library_Has_No_API_Changes.DotNet10_0.verified.txt index 79572a53073..5a112227cf4 100644 --- a/tests/TUnit.PublicAPI/Tests.Core_Library_Has_No_API_Changes.DotNet10_0.verified.txt +++ b/tests/TUnit.PublicAPI/Tests.Core_Library_Has_No_API_Changes.DotNet10_0.verified.txt @@ -1419,6 +1419,7 @@ namespace public new static .TestContext? Current { get; } public static string? OutputDirectory { get; } public static .> Parameters { get; } + public static string ResultsDirectory { get; } public static string? TestDirectory { get; } public static string WorkingDirectory { get; set; } public override string GetErrorOutput() { } diff --git a/tests/TUnit.PublicAPI/Tests.Core_Library_Has_No_API_Changes.DotNet8_0.verified.txt b/tests/TUnit.PublicAPI/Tests.Core_Library_Has_No_API_Changes.DotNet8_0.verified.txt index 2474a6a6a63..d88750b2f61 100644 --- a/tests/TUnit.PublicAPI/Tests.Core_Library_Has_No_API_Changes.DotNet8_0.verified.txt +++ b/tests/TUnit.PublicAPI/Tests.Core_Library_Has_No_API_Changes.DotNet8_0.verified.txt @@ -1419,6 +1419,7 @@ namespace public new static .TestContext? Current { get; } public static string? OutputDirectory { get; } public static .> Parameters { get; } + public static string ResultsDirectory { get; } public static string? TestDirectory { get; } public static string WorkingDirectory { get; set; } public override string GetErrorOutput() { } diff --git a/tests/TUnit.PublicAPI/Tests.Core_Library_Has_No_API_Changes.DotNet9_0.verified.txt b/tests/TUnit.PublicAPI/Tests.Core_Library_Has_No_API_Changes.DotNet9_0.verified.txt index 2b264f04779..44da96916cf 100644 --- a/tests/TUnit.PublicAPI/Tests.Core_Library_Has_No_API_Changes.DotNet9_0.verified.txt +++ b/tests/TUnit.PublicAPI/Tests.Core_Library_Has_No_API_Changes.DotNet9_0.verified.txt @@ -1419,6 +1419,7 @@ namespace public new static .TestContext? Current { get; } public static string? OutputDirectory { get; } public static .> Parameters { get; } + public static string ResultsDirectory { get; } public static string? TestDirectory { get; } public static string WorkingDirectory { get; set; } public override string GetErrorOutput() { } diff --git a/tests/TUnit.PublicAPI/Tests.Core_Library_Has_No_API_Changes.Net4_7.verified.txt b/tests/TUnit.PublicAPI/Tests.Core_Library_Has_No_API_Changes.Net4_7.verified.txt index 9d54d880263..6d64639253c 100644 --- a/tests/TUnit.PublicAPI/Tests.Core_Library_Has_No_API_Changes.Net4_7.verified.txt +++ b/tests/TUnit.PublicAPI/Tests.Core_Library_Has_No_API_Changes.Net4_7.verified.txt @@ -1363,6 +1363,7 @@ namespace public new static .TestContext? Current { get; } public static string? OutputDirectory { get; } public static .> Parameters { get; } + public static string ResultsDirectory { get; } public static string? TestDirectory { get; } public static string WorkingDirectory { get; set; } public override string GetErrorOutput() { } diff --git a/tests/TUnit.TestProject/TestContextTests.cs b/tests/TUnit.TestProject/TestContextTests.cs index b02c2147945..8aa01b51cfc 100644 --- a/tests/TUnit.TestProject/TestContextTests.cs +++ b/tests/TUnit.TestProject/TestContextTests.cs @@ -6,6 +6,17 @@ namespace TUnit.TestProject; [EngineTest(ExpectedResult.Pass)] public class TestContextTests { + [Test] + public async Task ResultsDirectory_Is_Exposed() + { + var resultsDirectory = TestContext.ResultsDirectory; + + await Assert.That(Path.IsPathFullyQualified(resultsDirectory)).IsTrue(); + await Assert.That(Directory.Exists(resultsDirectory)).IsTrue(); + await Assert.That(resultsDirectory) + .IsEqualTo(TestContext.Configuration.Get("platformOptions:resultDirectory")); + } + [Test] public async Task Test() {