From ff6f1debbdc43aee19a5817d6f963f8ca11f6fbc Mon Sep 17 00:00:00 2001
From: stijnmoreels <9039753+stijnmoreels@users.noreply.github.com>
Date: Tue, 15 Apr 2025 07:26:18 +0200
Subject: [PATCH 1/5] feat(logging): add logging support for TUnit test
framework
---
docs/preview/03-Features/03-logging.mdx | 40 +++++++-
.../Arcus.Testing.Logging.TUnit.csproj | 35 +++++++
.../Extensions/ILoggerBuilderExtensions.cs | 59 ++++++++++++
.../TUnitTestLogger.cs | 93 +++++++++++++++++++
.../Arcus.Testing.Tests.Unit.csproj | 1 +
.../Fixture/InMemoryTUnitTestLogger.cs | 36 +++++++
.../Logging/Fixture/MockTestContext.cs | 2 +-
.../Logging/ILoggerBuilderExtensionsTests.cs | 34 ++++++-
.../Logging/TUnitTestLoggerTests.cs | 29 ++++++
src/Arcus.Testing.sln | 7 ++
10 files changed, 331 insertions(+), 5 deletions(-)
create mode 100644 src/Arcus.Testing.Logging.TUnit/Arcus.Testing.Logging.TUnit.csproj
create mode 100644 src/Arcus.Testing.Logging.TUnit/Extensions/ILoggerBuilderExtensions.cs
create mode 100644 src/Arcus.Testing.Logging.TUnit/TUnitTestLogger.cs
create mode 100644 src/Arcus.Testing.Tests.Unit/Logging/Fixture/InMemoryTUnitTestLogger.cs
create mode 100644 src/Arcus.Testing.Tests.Unit/Logging/TUnitTestLoggerTests.cs
diff --git a/docs/preview/03-Features/03-logging.mdx b/docs/preview/03-Features/03-logging.mdx
index 7bfbc005..40f399de 100644
--- a/docs/preview/03-Features/03-logging.mdx
+++ b/docs/preview/03-Features/03-logging.mdx
@@ -10,7 +10,7 @@ This page describes functionality related to logging in tests.
- The `Arcus.Testing.Logging.Xunit` library provides a `XunitTestLogger` type that's an implementation of the abstracted Microsoft [`Ilogger`](https://docs.microsoft.com/en-us/dotnet/api/microsoft.extensions.logging)
+ The `Arcus.Testing.Logging.Xunit` library provides a `XunitTestLogger` type that's an implementation of the abstracted Microsoft [`ILogger`](https://docs.microsoft.com/en-us/dotnet/api/microsoft.extensions.logging)
inside the [xUnit](https://xunit.net/) test framework.
**Installation**
@@ -45,7 +45,7 @@ This page describes functionality related to logging in tests.
* [`AddXunitTestLogging`] extension to add a `ILoggerProvider` to a [Microsoft Logging](https://docs.microsoft.com/en-us/aspnet/core/fundamentals/logging/?view=aspnetcore-3.1) setup.
- The `Arcus.Testing.Logging.NUnit` library provides a `NUnitTestLogger` type that's an implementation of the abstracted Microsoft [`Ilogger`](https://docs.microsoft.com/en-us/dotnet/api/microsoft.extensions.logging)
+ The `Arcus.Testing.Logging.NUnit` library provides a `NUnitTestLogger` type that's an implementation of the abstracted Microsoft [`ILogger`](https://docs.microsoft.com/en-us/dotnet/api/microsoft.extensions.logging)
inside the [NUnit](https://nunit.org/) test framework.
**Installation**
@@ -80,7 +80,7 @@ This page describes functionality related to logging in tests.
* [`AddNUnitTestLogging`] extension to add a `ILoggerProvider` to a [Microsoft Logging](https://docs.microsoft.com/en-us/aspnet/core/fundamentals/logging/?view=aspnetcore-3.1) setup.
- The `Arcus.Testing.Logging.MSTest` library provides a `MSTestLogger` type that's an implementation of the abstracted Microsoft [`Ilogger`](https://docs.microsoft.com/en-us/dotnet/api/microsoft.extensions.logging)
+ The `Arcus.Testing.Logging.MSTest` library provides a `MSTestLogger` type that's an implementation of the abstracted Microsoft [`ILogger`](https://docs.microsoft.com/en-us/dotnet/api/microsoft.extensions.logging)
inside the [MSTest](https://learn.microsoft.com/en-us/dotnet/core/testing/unit-testing-with-mstest) test framework.
**Installation**
@@ -110,4 +110,38 @@ This page describes functionality related to logging in tests.
In the same fashion there is a:
* [`AddMSTestLogging`] extension to add a `ILoggerProvider` to a [Microsoft Logging](https://docs.microsoft.com/en-us/aspnet/core/fundamentals/logging/?view=aspnetcore-3.1) setup.
+
+ The `Arcus.Testing.Logging.TUnit` library provides a `TUnitTestLogger` type that's an implementation of the abstracted Microsoft [`ILogger`](https://docs.microsoft.com/en-us/dotnet/api/microsoft.extensions.logging) inside the [TUnit](https://tunit.dev/) test framework.
+
+ **Installation**
+
+ The following functionality is available when installing this package:
+
+ ```powershell
+ PM> Install-Package -Name Arcus.Testing.Logging.TUnit
+ ```
+
+ **Example**
+
+ Log messages written to the `ILogger` instance will be written to the `TUnit`'s test logger.
+
+ ```csharp
+ using Arcus.Testing;
+ using Microsoft.Extensions.Logging;
+ using TUnit.Core.Logging;
+
+ public class TestClass
+ {
+ [Test]
+ public async Task TestMethod()
+ {
+ var tunitTestLogger = TestContext.Current.GetDefaultLogger();
+ ILogger logger = new TUnitTestLogger(tunitTestLogger);
+ }
+ }
+ ```
+
+ In the same fashion there is a:
+ * [`AddTUnitTestLogging`] extension to add a `ILoggerProvider` to a [Microsoft Logging](https://docs.microsoft.com/en-us/aspnet/core/fundamentals/logging/?view=aspnetcore-3.1) setup.
+
\ No newline at end of file
diff --git a/src/Arcus.Testing.Logging.TUnit/Arcus.Testing.Logging.TUnit.csproj b/src/Arcus.Testing.Logging.TUnit/Arcus.Testing.Logging.TUnit.csproj
new file mode 100644
index 00000000..11a3a8df
--- /dev/null
+++ b/src/Arcus.Testing.Logging.TUnit/Arcus.Testing.Logging.TUnit.csproj
@@ -0,0 +1,35 @@
+
+
+
+ net8.0
+ Arcus.Testing
+ Arcus
+ Arcus
+ Provides logging capabilities during testing within the TUnit framework
+ Copyright (c) Arcus
+ https://github.com/arcus-azure/arcus.testing
+ https://github.com/arcus-azure/arcus.testing
+ LICENSE
+ icon.png
+ README.md
+ Git
+ Azure;Testing
+ Arcus.Testing.Logging.TUnit
+ true
+ true
+ true
+ $(WarningsNotAsErrors);NU1901;NU1902;NU1903;NU1904
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Arcus.Testing.Logging.TUnit/Extensions/ILoggerBuilderExtensions.cs b/src/Arcus.Testing.Logging.TUnit/Extensions/ILoggerBuilderExtensions.cs
new file mode 100644
index 00000000..1be83da7
--- /dev/null
+++ b/src/Arcus.Testing.Logging.TUnit/Extensions/ILoggerBuilderExtensions.cs
@@ -0,0 +1,59 @@
+using System;
+using Arcus.Testing;
+using ITUnitLogger = TUnit.Core.Logging.ILogger;
+
+// ReSharper disable once CheckNamespace
+namespace Microsoft.Extensions.Logging
+{
+ ///
+ /// Extensions on the related to logging.
+ ///
+ // ReSharper disable once InconsistentNaming
+ public static class ILoggerBuilderExtensions
+ {
+ ///
+ /// Adds the logging messages from the given TUnit as a provider to the .
+ ///
+ /// The logging builder to add the NUnit logging test messages to.
+ /// The TUnit test writer to write custom test output.
+ /// Thrown when the or the is null.
+ public static ILoggingBuilder AddTUnitTestLogging(this ILoggingBuilder builder, ITUnitLogger outputWriter)
+ {
+ ArgumentNullException.ThrowIfNull(builder);
+ ArgumentNullException.ThrowIfNull(outputWriter);
+
+ return builder.AddProvider(new TUnitLoggerProvider(outputWriter));
+ }
+
+ [ProviderAlias("TUnit")]
+ private sealed class TUnitLoggerProvider : ILoggerProvider
+ {
+ private readonly ITUnitLogger _testLogger;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ internal TUnitLoggerProvider(ITUnitLogger testLogger)
+ {
+ _testLogger = testLogger;
+ }
+
+ ///
+ /// Creates a new instance.
+ ///
+ /// The category name for messages produced by the logger.
+ /// The instance of that was created.
+ public ILogger CreateLogger(string categoryName)
+ {
+ return new TUnitTestLogger(_testLogger);
+ }
+
+ ///
+ /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
+ ///
+ public void Dispose()
+ {
+ }
+ }
+ }
+}
diff --git a/src/Arcus.Testing.Logging.TUnit/TUnitTestLogger.cs b/src/Arcus.Testing.Logging.TUnit/TUnitTestLogger.cs
new file mode 100644
index 00000000..c532b3a9
--- /dev/null
+++ b/src/Arcus.Testing.Logging.TUnit/TUnitTestLogger.cs
@@ -0,0 +1,93 @@
+using System;
+using Microsoft.Extensions.Logging;
+using ITUnitLogger = TUnit.Core.Logging.ILogger;
+using TUnitLogLevel = TUnit.Core.Logging.LogLevel;
+
+namespace Arcus.Testing
+{
+ ///
+ /// representation of a TUnit logger.
+ ///
+ public class TUnitTestLogger : ILogger
+ {
+ private readonly ITUnitLogger _outputWriter;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The TUnit test writer to write custom test output.
+ /// Thrown when the is null.
+ public TUnitTestLogger(ITUnitLogger outputWriter)
+ {
+ ArgumentNullException.ThrowIfNull(outputWriter);
+ _outputWriter = outputWriter;
+ }
+
+ ///
+ /// Writes a log entry.
+ ///
+ /// Entry will be written on this level.
+ /// Id of the event.
+ /// The entry to be written. Can be also an object.
+ /// The exception related to this entry.
+ /// Function to create a message of the and .
+ /// The type of the object to be written.
+ public void Log(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func formatter)
+ {
+ TUnitLogLevel level = ConvertToTUnitLogLevel(logLevel);
+
+ _outputWriter.Log(level, state, exception, formatter);
+ }
+
+ ///
+ /// Checks if the given is enabled.
+ ///
+ /// Level to be checked.
+ /// true if enabled.
+ public bool IsEnabled(LogLevel logLevel)
+ {
+ TUnitLogLevel level = ConvertToTUnitLogLevel(logLevel);
+ return _outputWriter.IsEnabled(level);
+ }
+
+ private static TUnitLogLevel ConvertToTUnitLogLevel(LogLevel logLevel)
+ {
+ return logLevel switch
+ {
+ LogLevel.Critical => TUnitLogLevel.Critical,
+ LogLevel.Error => TUnitLogLevel.Error,
+ LogLevel.Warning => TUnitLogLevel.Warning,
+ LogLevel.Information => TUnitLogLevel.Information,
+ LogLevel.Debug => TUnitLogLevel.Debug,
+ LogLevel.Trace => TUnitLogLevel.Trace,
+ LogLevel.None => TUnitLogLevel.None,
+ _ => TUnitLogLevel.None
+ };
+ }
+
+ ///
+ /// Begins a logical operation scope.
+ ///
+ /// The identifier for the scope.
+ /// The type of the state to begin scope for.
+ /// An that ends the logical operation scope on dispose.
+ public IDisposable BeginScope(TState state) where TState : notnull
+ {
+ return null;
+ }
+ }
+
+ ///
+ /// representation of a TUnit logger.
+ ///
+ /// The type whose name is used for the logger category name.
+ public class TUnitTestLogger : TUnitTestLogger, ILogger
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public TUnitTestLogger(ITUnitLogger outputWriter) : base(outputWriter)
+ {
+ }
+ }
+}
diff --git a/src/Arcus.Testing.Tests.Unit/Arcus.Testing.Tests.Unit.csproj b/src/Arcus.Testing.Tests.Unit/Arcus.Testing.Tests.Unit.csproj
index 7bdce1dd..d8c921cb 100644
--- a/src/Arcus.Testing.Tests.Unit/Arcus.Testing.Tests.Unit.csproj
+++ b/src/Arcus.Testing.Tests.Unit/Arcus.Testing.Tests.Unit.csproj
@@ -24,6 +24,7 @@
+
diff --git a/src/Arcus.Testing.Tests.Unit/Logging/Fixture/InMemoryTUnitTestLogger.cs b/src/Arcus.Testing.Tests.Unit/Logging/Fixture/InMemoryTUnitTestLogger.cs
new file mode 100644
index 00000000..a8bd9fb1
--- /dev/null
+++ b/src/Arcus.Testing.Tests.Unit/Logging/Fixture/InMemoryTUnitTestLogger.cs
@@ -0,0 +1,36 @@
+using System;
+using System.Collections.ObjectModel;
+using System.Threading.Tasks;
+using TUnit.Core.Logging;
+using Xunit;
+
+namespace Arcus.Testing.Tests.Unit.Logging.Fixture
+{
+ public class MockTUnitTestLogger : ILogger
+ {
+ internal Collection<(LogLevel level, string message)> Logs { get; } = [];
+
+ public ValueTask LogAsync(LogLevel logLevel, TState state, Exception exception, Func formatter)
+ {
+ throw new NotImplementedException();
+ }
+
+ public void Log(LogLevel logLevel, TState state, Exception exception, Func formatter)
+ {
+ Logs.Add((logLevel, formatter(state, exception)));
+ }
+
+ public bool IsEnabled(LogLevel logLevel)
+ {
+ throw new NotImplementedException();
+ }
+
+ ///
+ /// Verifies that there was a written for the given to this logger.
+ ///
+ public void VerifyWritten(Microsoft.Extensions.Logging.LogLevel level, string message)
+ {
+ Assert.Contains(Logs, log => (int) level == (int) log.level && message == log.message);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Arcus.Testing.Tests.Unit/Logging/Fixture/MockTestContext.cs b/src/Arcus.Testing.Tests.Unit/Logging/Fixture/MockTestContext.cs
index 4acca961..14be0bb2 100644
--- a/src/Arcus.Testing.Tests.Unit/Logging/Fixture/MockTestContext.cs
+++ b/src/Arcus.Testing.Tests.Unit/Logging/Fixture/MockTestContext.cs
@@ -1,8 +1,8 @@
using System;
using System.Collections;
using System.Collections.ObjectModel;
-using Microsoft.VisualStudio.TestTools.UnitTesting;
using Assert = Xunit.Assert;
+using TestContext = Microsoft.VisualStudio.TestTools.UnitTesting.TestContext;
namespace Arcus.Testing.Tests.Unit.Logging.Fixture
{
diff --git a/src/Arcus.Testing.Tests.Unit/Logging/ILoggerBuilderExtensionsTests.cs b/src/Arcus.Testing.Tests.Unit/Logging/ILoggerBuilderExtensionsTests.cs
index 7a74910e..eddb60a6 100644
--- a/src/Arcus.Testing.Tests.Unit/Logging/ILoggerBuilderExtensionsTests.cs
+++ b/src/Arcus.Testing.Tests.Unit/Logging/ILoggerBuilderExtensionsTests.cs
@@ -149,12 +149,44 @@ public void AddXunitTestLogging_WithoutXunitTestLogger_Throws()
{
// Arrange
var builder = new HostBuilder();
-
+
// Act
builder.ConfigureLogging(logging => logging.AddXunitTestLogging(outputWriter: null));
// Assert
Assert.ThrowsAny(() => builder.Build());
}
+
+ [Fact]
+ public void AddTUnitTestLogging_WithTestLogger_LogsMessage()
+ {
+ // Arrange
+ var mockLogger = new MockTUnitTestLogger();
+ var builder = new HostBuilder();
+
+ // Act
+ builder.ConfigureLogging(logging => logging.AddTUnitTestLogging(mockLogger));
+
+ // Assert
+ using IHost host = builder.Build();
+ var logger = host.Services.GetRequiredService>();
+
+ string expected = Bogus.Lorem.Sentence();
+ logger.LogInformation(expected);
+ mockLogger.VerifyWritten(LogLevel.Information, expected);
+ }
+
+ [Fact]
+ public void AddTUnitTestLogging_WithoutTestLogger_Throws()
+ {
+ // Arrange
+ var builder = new HostBuilder();
+
+ // Act
+ builder.ConfigureLogging(logging => logging.AddTUnitTestLogging(outputWriter: null));
+
+ // Assert
+ Assert.ThrowsAny(() => builder.Build());
+ }
}
}
diff --git a/src/Arcus.Testing.Tests.Unit/Logging/TUnitTestLoggerTests.cs b/src/Arcus.Testing.Tests.Unit/Logging/TUnitTestLoggerTests.cs
new file mode 100644
index 00000000..5723a432
--- /dev/null
+++ b/src/Arcus.Testing.Tests.Unit/Logging/TUnitTestLoggerTests.cs
@@ -0,0 +1,29 @@
+using Arcus.Testing.Tests.Unit.Logging.Fixture;
+using Bogus;
+using Microsoft.Extensions.Logging;
+using Xunit;
+
+namespace Arcus.Testing.Tests.Unit.Logging
+{
+ public class TUnitTestLoggerTests
+ {
+ private static readonly Faker Bogus = new();
+
+ [Fact]
+ public void Log_WithLevel_SucceedsWithMicrosoftLevel()
+ {
+ // Arrange
+ string expectedMessage = Bogus.Lorem.Sentence();
+ var expectedLevel = Bogus.PickRandom();
+
+ var mockLogger = new MockTUnitTestLogger();
+ var logger = new TUnitTestLogger(mockLogger);
+
+ // Act
+ logger.Log(expectedLevel, expectedMessage);
+
+ // Assert
+ mockLogger.VerifyWritten(expectedLevel, expectedMessage);
+ }
+ }
+}
diff --git a/src/Arcus.Testing.sln b/src/Arcus.Testing.sln
index 5ddbe02d..bdc47a85 100644
--- a/src/Arcus.Testing.sln
+++ b/src/Arcus.Testing.sln
@@ -48,6 +48,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Messaging", "Messaging", "{4992B48D-3C17-45A9-979B-B79CE1E987CB}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Arcus.Testing.Logging.TUnit", "Arcus.Testing.Logging.TUnit\Arcus.Testing.Logging.TUnit.csproj", "{2A0C03CC-3D26-4AC4-A4B8-3B7F4DB418EF}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -108,6 +110,10 @@ Global
{74AB9F6E-791F-4609-96BD-15420C25AA72}.Debug|Any CPU.Build.0 = Debug|Any CPU
{74AB9F6E-791F-4609-96BD-15420C25AA72}.Release|Any CPU.ActiveCfg = Release|Any CPU
{74AB9F6E-791F-4609-96BD-15420C25AA72}.Release|Any CPU.Build.0 = Release|Any CPU
+ {2A0C03CC-3D26-4AC4-A4B8-3B7F4DB418EF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {2A0C03CC-3D26-4AC4-A4B8-3B7F4DB418EF}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {2A0C03CC-3D26-4AC4-A4B8-3B7F4DB418EF}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {2A0C03CC-3D26-4AC4-A4B8-3B7F4DB418EF}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -125,6 +131,7 @@ Global
{4F199527-761E-4F8A-AB44-8DE8D030518F} = {4992B48D-3C17-45A9-979B-B79CE1E987CB}
{54A67E0F-270B-4979-9E72-C68EA8222F89} = {FA2E21E0-953E-4B84-9C47-C4F0A3833E4E}
{74AB9F6E-791F-4609-96BD-15420C25AA72} = {FA2E21E0-953E-4B84-9C47-C4F0A3833E4E}
+ {2A0C03CC-3D26-4AC4-A4B8-3B7F4DB418EF} = {45B1870D-C19A-4680-BDD8-2F670C793BC2}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {E5382820-51FF-4B00-92BE-C78E80EA0841}
From 18be50c48123aa4a1203572cbb4dc47cd5a5b1e9 Mon Sep 17 00:00:00 2001
From: stijnmoreels <9039753+stijnmoreels@users.noreply.github.com>
Date: Tue, 15 Apr 2025 09:49:33 +0200
Subject: [PATCH 2/5] chore: remove unnecessary namespace
---
src/Arcus.Testing.Tests.Unit/Logging/TUnitTestLoggerTests.cs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/Arcus.Testing.Tests.Unit/Logging/TUnitTestLoggerTests.cs b/src/Arcus.Testing.Tests.Unit/Logging/TUnitTestLoggerTests.cs
index 5723a432..7e5fd6fa 100644
--- a/src/Arcus.Testing.Tests.Unit/Logging/TUnitTestLoggerTests.cs
+++ b/src/Arcus.Testing.Tests.Unit/Logging/TUnitTestLoggerTests.cs
@@ -14,7 +14,7 @@ public void Log_WithLevel_SucceedsWithMicrosoftLevel()
{
// Arrange
string expectedMessage = Bogus.Lorem.Sentence();
- var expectedLevel = Bogus.PickRandom();
+ var expectedLevel = Bogus.PickRandom();
var mockLogger = new MockTUnitTestLogger();
var logger = new TUnitTestLogger(mockLogger);
From 2eaa54da210bf4d7ccf88e3e0680fcd6a0ff99d6 Mon Sep 17 00:00:00 2001
From: Stijn Moreels <9039753+stijnmoreels@users.noreply.github.com>
Date: Thu, 19 Jun 2025 09:21:00 +0200
Subject: [PATCH 3/5] fix(merge): finalize merge with 'main'
---
src/Arcus.Testing.Tests.Unit/Logging/Fixture/MockTestContext.cs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/Arcus.Testing.Tests.Unit/Logging/Fixture/MockTestContext.cs b/src/Arcus.Testing.Tests.Unit/Logging/Fixture/MockTestContext.cs
index 28f5bb6a..f43af84b 100644
--- a/src/Arcus.Testing.Tests.Unit/Logging/Fixture/MockTestContext.cs
+++ b/src/Arcus.Testing.Tests.Unit/Logging/Fixture/MockTestContext.cs
@@ -1,7 +1,7 @@
using System;
using System.Collections;
using System.Collections.ObjectModel;
-using Assert = Xunit.Assert;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
using TestContext = Microsoft.VisualStudio.TestTools.UnitTesting.TestContext;
namespace Arcus.Testing.Tests.Unit.Logging.Fixture
From 6a14363fd3625e844ccb5fcd0a825575e38b85a8 Mon Sep 17 00:00:00 2001
From: Stijn Moreels <9039753+stijnmoreels@users.noreply.github.com>
Date: Wed, 19 Nov 2025 18:55:22 +0100
Subject: [PATCH 4/5] Update Arcus.Testing.Logging.TUnit.csproj
---
.../Arcus.Testing.Logging.TUnit.csproj | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/src/Arcus.Testing.Logging.TUnit/Arcus.Testing.Logging.TUnit.csproj b/src/Arcus.Testing.Logging.TUnit/Arcus.Testing.Logging.TUnit.csproj
index 11a3a8df..8787fb0b 100644
--- a/src/Arcus.Testing.Logging.TUnit/Arcus.Testing.Logging.TUnit.csproj
+++ b/src/Arcus.Testing.Logging.TUnit/Arcus.Testing.Logging.TUnit.csproj
@@ -1,7 +1,7 @@
- net8.0
+ net10.0
Arcus.Testing
Arcus
Arcus
@@ -28,8 +28,8 @@
-
-
+
+
From 7084b4fbc58e20c0560d61c172d875e827c04dcf Mon Sep 17 00:00:00 2001
From: Stijn Moreels <9039753+stijnmoreels@users.noreply.github.com>
Date: Thu, 4 Dec 2025 07:31:29 +0100
Subject: [PATCH 5/5] chore(tunit): include scope + category names in tunit
logging
---
.../Arcus.Testing.Logging.TUnit.csproj | 6 ++++
.../Extensions/ILoggerBuilderExtensions.cs | 19 ++++++++--
.../TUnitTestLogger.cs | 35 ++++++++++++++++---
.../Fixture/InMemoryTUnitTestLogger.cs | 15 ++++++--
.../Logging/ILoggerBuilderExtensionsTests.cs | 5 ++-
.../Logging/TUnitTestLoggerTests.cs | 5 +--
6 files changed, 72 insertions(+), 13 deletions(-)
diff --git a/src/Arcus.Testing.Logging.TUnit/Arcus.Testing.Logging.TUnit.csproj b/src/Arcus.Testing.Logging.TUnit/Arcus.Testing.Logging.TUnit.csproj
index 11a3a8df..15e724a6 100644
--- a/src/Arcus.Testing.Logging.TUnit/Arcus.Testing.Logging.TUnit.csproj
+++ b/src/Arcus.Testing.Logging.TUnit/Arcus.Testing.Logging.TUnit.csproj
@@ -19,8 +19,14 @@
true
true
$(WarningsNotAsErrors);NU1901;NU1902;NU1903;NU1904
+ S1133
+ All
+
+
+
+
diff --git a/src/Arcus.Testing.Logging.TUnit/Extensions/ILoggerBuilderExtensions.cs b/src/Arcus.Testing.Logging.TUnit/Extensions/ILoggerBuilderExtensions.cs
index 1be83da7..70c2ea8e 100644
--- a/src/Arcus.Testing.Logging.TUnit/Extensions/ILoggerBuilderExtensions.cs
+++ b/src/Arcus.Testing.Logging.TUnit/Extensions/ILoggerBuilderExtensions.cs
@@ -22,13 +22,17 @@ public static ILoggingBuilder AddTUnitTestLogging(this ILoggingBuilder builder,
ArgumentNullException.ThrowIfNull(builder);
ArgumentNullException.ThrowIfNull(outputWriter);
- return builder.AddProvider(new TUnitLoggerProvider(outputWriter));
+#pragma warning disable CA2000 // Responsibility of disposing the created object is transferred to the caller
+ var provider = new TUnitLoggerProvider(outputWriter);
+#pragma warning restore CA2000
+ return builder.AddProvider(provider);
}
[ProviderAlias("TUnit")]
- private sealed class TUnitLoggerProvider : ILoggerProvider
+ private sealed class TUnitLoggerProvider : ILoggerProvider, ISupportExternalScope
{
private readonly ITUnitLogger _testLogger;
+ private IExternalScopeProvider _scopeProvider;
///
/// Initializes a new instance of the class.
@@ -45,7 +49,16 @@ internal TUnitLoggerProvider(ITUnitLogger testLogger)
/// The instance of that was created.
public ILogger CreateLogger(string categoryName)
{
- return new TUnitTestLogger(_testLogger);
+ return new TUnitTestLogger(_testLogger, _scopeProvider, categoryName);
+ }
+
+ ///
+ /// Sets external scope information source for logger provider.
+ ///
+ /// The provider of scope data.
+ public void SetScopeProvider(IExternalScopeProvider scopeProvider)
+ {
+ _scopeProvider = scopeProvider;
}
///
diff --git a/src/Arcus.Testing.Logging.TUnit/TUnitTestLogger.cs b/src/Arcus.Testing.Logging.TUnit/TUnitTestLogger.cs
index c532b3a9..6341ddfb 100644
--- a/src/Arcus.Testing.Logging.TUnit/TUnitTestLogger.cs
+++ b/src/Arcus.Testing.Logging.TUnit/TUnitTestLogger.cs
@@ -11,6 +11,8 @@ namespace Arcus.Testing
public class TUnitTestLogger : ILogger
{
private readonly ITUnitLogger _outputWriter;
+ private readonly IExternalScopeProvider _scopeProvider;
+ private readonly string _categoryName;
///
/// Initializes a new instance of the class.
@@ -18,9 +20,23 @@ public class TUnitTestLogger : ILogger
/// The TUnit test writer to write custom test output.
/// Thrown when the is null.
public TUnitTestLogger(ITUnitLogger outputWriter)
+ : this(outputWriter, scopeProvider: null, categoryName: null)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The TUnit test writer to write custom test output.
+ /// The instance to provide logging scopes.
+ /// The category name for messages produced by the logger.
+ /// Thrown when the is null.
+ internal TUnitTestLogger(ITUnitLogger outputWriter, IExternalScopeProvider scopeProvider, string categoryName)
{
ArgumentNullException.ThrowIfNull(outputWriter);
_outputWriter = outputWriter;
+ _scopeProvider = scopeProvider;
+ _categoryName = categoryName;
}
///
@@ -36,7 +52,18 @@ public void Log(LogLevel logLevel, EventId eventId, TState state, Except
{
TUnitLogLevel level = ConvertToTUnitLogLevel(logLevel);
- _outputWriter.Log(level, state, exception, formatter);
+ ArgumentNullException.ThrowIfNull(formatter);
+ string message = formatter(state, exception);
+
+ var builder = new LogMessageBuilder(logLevel);
+ builder.AddCategory(_categoryName)
+ .AddUserMessage(message)
+ .AddException(exception);
+
+ _scopeProvider?.ForEachScope((st, lb) => lb.AddScope(st), builder);
+
+ string result = builder.ToString();
+ _outputWriter.Log(level, result, exception, formatter: (st, _) => st);
}
///
@@ -60,7 +87,6 @@ private static TUnitLogLevel ConvertToTUnitLogLevel(LogLevel logLevel)
LogLevel.Information => TUnitLogLevel.Information,
LogLevel.Debug => TUnitLogLevel.Debug,
LogLevel.Trace => TUnitLogLevel.Trace,
- LogLevel.None => TUnitLogLevel.None,
_ => TUnitLogLevel.None
};
}
@@ -73,7 +99,7 @@ private static TUnitLogLevel ConvertToTUnitLogLevel(LogLevel logLevel)
/// An that ends the logical operation scope on dispose.
public IDisposable BeginScope(TState state) where TState : notnull
{
- return null;
+ return _scopeProvider?.Push(state);
}
}
@@ -86,7 +112,8 @@ public class TUnitTestLogger : TUnitTestLogger, ILogger
/// Initializes a new instance of the class.
///
- public TUnitTestLogger(ITUnitLogger outputWriter) : base(outputWriter)
+ public TUnitTestLogger(ITUnitLogger outputWriter)
+ : base(outputWriter, scopeProvider: null, categoryName: typeof(TCategoryName).FullName)
{
}
}
diff --git a/src/Arcus.Testing.Tests.Unit/Logging/Fixture/InMemoryTUnitTestLogger.cs b/src/Arcus.Testing.Tests.Unit/Logging/Fixture/InMemoryTUnitTestLogger.cs
index a8bd9fb1..95337d78 100644
--- a/src/Arcus.Testing.Tests.Unit/Logging/Fixture/InMemoryTUnitTestLogger.cs
+++ b/src/Arcus.Testing.Tests.Unit/Logging/Fixture/InMemoryTUnitTestLogger.cs
@@ -28,9 +28,18 @@ public bool IsEnabled(LogLevel logLevel)
///
/// Verifies that there was a written for the given to this logger.
///
- public void VerifyWritten(Microsoft.Extensions.Logging.LogLevel level, string message)
+ public void VerifyWritten(Microsoft.Extensions.Logging.LogLevel level, string message, Exception exception = null, string state = null)
{
- Assert.Contains(Logs, log => (int) level == (int) log.level && message == log.message);
+ Assert.Contains(Logs, log =>
+ {
+ bool hasException = exception is null || log.message.Contains(exception.Message);
+ bool hasState = state is null || log.message.Contains(state);
+
+ return (int) level == (int) log.level
+ && log.message.Contains(message)
+ && hasState
+ && hasException;
+ });
}
}
-}
\ No newline at end of file
+}
diff --git a/src/Arcus.Testing.Tests.Unit/Logging/ILoggerBuilderExtensionsTests.cs b/src/Arcus.Testing.Tests.Unit/Logging/ILoggerBuilderExtensionsTests.cs
index 3d41986d..9cd853c1 100644
--- a/src/Arcus.Testing.Tests.Unit/Logging/ILoggerBuilderExtensionsTests.cs
+++ b/src/Arcus.Testing.Tests.Unit/Logging/ILoggerBuilderExtensionsTests.cs
@@ -185,9 +185,12 @@ public void AddTUnitTestLogging_WithTestLogger_LogsMessage()
using IHost host = builder.Build();
var logger = host.Services.GetRequiredService>();
+ string state = Bogus.Lorem.Word();
+ using var _ = logger.BeginScope(state);
+
string expected = Bogus.Lorem.Sentence();
logger.LogInformation(expected);
- mockLogger.VerifyWritten(LogLevel.Information, expected);
+ mockLogger.VerifyWritten(LogLevel.Information, expected, state: state);
}
[Fact]
diff --git a/src/Arcus.Testing.Tests.Unit/Logging/TUnitTestLoggerTests.cs b/src/Arcus.Testing.Tests.Unit/Logging/TUnitTestLoggerTests.cs
index 7e5fd6fa..d8f6e88a 100644
--- a/src/Arcus.Testing.Tests.Unit/Logging/TUnitTestLoggerTests.cs
+++ b/src/Arcus.Testing.Tests.Unit/Logging/TUnitTestLoggerTests.cs
@@ -15,15 +15,16 @@ public void Log_WithLevel_SucceedsWithMicrosoftLevel()
// Arrange
string expectedMessage = Bogus.Lorem.Sentence();
var expectedLevel = Bogus.PickRandom();
+ var exception = Bogus.System.Exception().OrNull(Bogus);
var mockLogger = new MockTUnitTestLogger();
var logger = new TUnitTestLogger(mockLogger);
// Act
- logger.Log(expectedLevel, expectedMessage);
+ logger.Log(expectedLevel, exception, expectedMessage);
// Assert
- mockLogger.VerifyWritten(expectedLevel, expectedMessage);
+ mockLogger.VerifyWritten(expectedLevel, expectedMessage, exception);
}
}
}