From 92b3dd31205954fa638000bacfcf2bd90171c5c7 Mon Sep 17 00:00:00 2001 From: Michael Mortensen Date: Wed, 9 Apr 2025 20:05:33 +0200 Subject: [PATCH 1/6] :arrow_up: bump dependencies --- Directory.Packages.props | 16 ++++++++-------- testenvironments.json | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index 0270c07..f94a664 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -7,7 +7,7 @@ - + @@ -24,12 +24,12 @@ - - - - - - + + + + + + @@ -37,6 +37,6 @@ - + \ No newline at end of file diff --git a/testenvironments.json b/testenvironments.json index e3ebb1b..cb17045 100644 --- a/testenvironments.json +++ b/testenvironments.json @@ -9,7 +9,7 @@ { "name": "Docker-Ubuntu", "type": "docker", - "dockerImage": "gimlichael/ubuntu-testrunner:mono-net8.0.407-9.0.202" + "dockerImage": "gimlichael/ubuntu-testrunner:mono-net8.0.408-9.0.203" } ] } From 37530d48181d8dc5d359e0e2068d05670d20247f Mon Sep 17 00:00:00 2001 From: Michael Mortensen Date: Wed, 9 Apr 2025 21:01:34 +0200 Subject: [PATCH 2/6] :sparkles: support for xunit test logging without dependency to ITestOutputHelper --- .../ServiceCollectionExtensions.cs | 47 ++++++++++++++----- .../XunitTestLogger.cs | 2 +- .../XunitTestLoggerProvider.cs | 4 ++ 3 files changed, 39 insertions(+), 14 deletions(-) diff --git a/src/Codebelt.Extensions.Xunit.Hosting/ServiceCollectionExtensions.cs b/src/Codebelt.Extensions.Xunit.Hosting/ServiceCollectionExtensions.cs index ea06662..d60624c 100644 --- a/src/Codebelt.Extensions.Xunit.Hosting/ServiceCollectionExtensions.cs +++ b/src/Codebelt.Extensions.Xunit.Hosting/ServiceCollectionExtensions.cs @@ -11,6 +11,27 @@ namespace Codebelt.Extensions.Xunit.Hosting /// public static class ServiceCollectionExtensions { + /// + /// Adds a unit test optimized implementation of logging to the collection. + /// + /// The to extend. + /// The that specifies the minimum level to include for the logging. + /// A reference to so that additional configuration calls can be chained. + /// + /// cannot be null. + /// + public static IServiceCollection AddXunitTestLogging(this IServiceCollection services, LogLevel minimumLevel = LogLevel.Trace) + { + if (services == null) { throw new ArgumentNullException(nameof(services)); } + services.AddLogging(builder => + { + builder.SetMinimumLevel(minimumLevel); + builder.AddProvider(new XunitTestLoggerProvider()); + }); + + return services; + } + /// /// Adds a unit test optimized implementation of output logging to the collection. /// @@ -22,14 +43,14 @@ public static class ServiceCollectionExtensions /// cannot be null -or- /// cannot be null. /// - public static IServiceCollection AddXunitTestLogging(this IServiceCollection services, ITestOutputHelper output, LogLevel minimumLevel = LogLevel.Trace) - { + public static IServiceCollection AddXunitTestLogging(this IServiceCollection services, ITestOutputHelper output, LogLevel minimumLevel = LogLevel.Trace) + { if (services == null) { throw new ArgumentNullException(nameof(services)); } if (output == null) { throw new ArgumentNullException(nameof(output)); } if (services.Any(sd => sd.ServiceType == typeof(ITestOutputHelperAccessor))) { - services.AddLogging(builder => - { + services.AddLogging(builder => + { builder.SetMinimumLevel(minimumLevel); builder.Services.AddSingleton(provider => { @@ -37,19 +58,19 @@ public static IServiceCollection AddXunitTestLogging(this IServiceCollection ser accessor.TestOutput = output; return new XunitTestLoggerProvider(accessor); }); - }); + }); } else { - services.AddLogging(builder => - { - builder.SetMinimumLevel(minimumLevel); - builder.AddProvider(new XunitTestLoggerProvider(output)); - }); + services.AddLogging(builder => + { + builder.SetMinimumLevel(minimumLevel); + builder.AddProvider(new XunitTestLoggerProvider(output)); + }); } - return services; - } - + return services; + } + /// /// Adds a default implementation of to the collection. /// diff --git a/src/Codebelt.Extensions.Xunit.Hosting/XunitTestLogger.cs b/src/Codebelt.Extensions.Xunit.Hosting/XunitTestLogger.cs index aae9ec2..66d7e5c 100644 --- a/src/Codebelt.Extensions.Xunit.Hosting/XunitTestLogger.cs +++ b/src/Codebelt.Extensions.Xunit.Hosting/XunitTestLogger.cs @@ -41,7 +41,7 @@ public void Log(LogLevel logLevel, EventId eventId, TState state, Except } else { - _output.WriteLine(message); + _output?.WriteLine(message); } } diff --git a/src/Codebelt.Extensions.Xunit.Hosting/XunitTestLoggerProvider.cs b/src/Codebelt.Extensions.Xunit.Hosting/XunitTestLoggerProvider.cs index 8ec44fe..00a52a1 100644 --- a/src/Codebelt.Extensions.Xunit.Hosting/XunitTestLoggerProvider.cs +++ b/src/Codebelt.Extensions.Xunit.Hosting/XunitTestLoggerProvider.cs @@ -12,6 +12,10 @@ internal sealed class XunitTestLoggerProvider : InMemoryTestStore Date: Wed, 9 Apr 2025 21:02:03 +0200 Subject: [PATCH 3/6] :white_check_mark: complemental test for xunit test logger --- .../ServiceCollectionExtensions.cs | 56 ++++++++++++++++++- 1 file changed, 55 insertions(+), 1 deletion(-) diff --git a/test/Codebelt.Extensions.Xunit.Hosting.Tests/ServiceCollectionExtensions.cs b/test/Codebelt.Extensions.Xunit.Hosting.Tests/ServiceCollectionExtensions.cs index 65751eb..4fad74c 100644 --- a/test/Codebelt.Extensions.Xunit.Hosting.Tests/ServiceCollectionExtensions.cs +++ b/test/Codebelt.Extensions.Xunit.Hosting.Tests/ServiceCollectionExtensions.cs @@ -18,7 +18,7 @@ public ServiceCollectionExtensions(ITestOutputHelper output) : base(output) public void AddXunitTestLogging_ShouldAddXunitTestLogging() { var services = new ServiceCollection(); - services.AddXunitTestLogging(TestOutput); + services.AddXunitTestLogging(); var provider = services.BuildServiceProvider(); @@ -67,7 +67,61 @@ public void AddXunitTestLogging_ShouldAddXunitTestLogging() entry => Assert.Equal("Information: SUT", entry.ToString()), entry => Assert.Equal("Warning: SUT", entry.ToString()), entry => Assert.Equal("Information: Unique message for logger2.", entry.ToString())); + } + + [Fact] + public void AddXunitTestLogging_ShouldAddXunitTestLoggingWithTestOutput() + { + var services = new ServiceCollection(); + services.AddXunitTestLogging(TestOutput); + + var provider = services.BuildServiceProvider(); + + var logger1 = provider.GetRequiredService>(); + var loggerStore1 = logger1.GetTestStore(); + var loggerStore1Duplicate = logger1.GetTestStore(typeof(ServiceCollectionExtensions).FullName!.ToLowerInvariant()); + + var logger2 = provider.GetRequiredService>(); + var loggerStore2 = logger2.GetTestStore(null); // all loggers + + logger1.LogCritical("SUT"); + logger1.LogTrace("SUT"); + logger1.LogDebug("SUT"); + logger1.LogError("SUT"); + logger1.LogInformation("SUT"); + logger1.LogWarning("SUT"); + + logger2.LogInformation("Unique message for logger2."); + + Assert.Throws(() => logger2.GetTestStore("InvalidValue")); + Assert.Equal(loggerStore1, loggerStore1Duplicate); + + Assert.NotNull(logger1); + Assert.NotNull(loggerStore1); + + Assert.NotNull(logger2); + Assert.NotNull(loggerStore2); + + Assert.Equal(6, loggerStore1.Query().Count()); + Assert.Equal(7, loggerStore2.Query().Count()); + + Assert.Collection(loggerStore1.Query(), + entry => Assert.Equal("Critical: SUT", entry.ToString()), + entry => Assert.Equal("Trace: SUT", entry.ToString()), + entry => Assert.Equal("Debug: SUT", entry.ToString()), + entry => Assert.Equal("Error: SUT", entry.ToString()), + entry => Assert.Equal("Information: SUT", entry.ToString()), + entry => Assert.Equal("Warning: SUT", entry.ToString())); + + Assert.Collection(loggerStore2.Query(), + entry => Assert.Equal("Critical: SUT", entry.ToString()), + entry => Assert.Equal("Trace: SUT", entry.ToString()), + entry => Assert.Equal("Debug: SUT", entry.ToString()), + entry => Assert.Equal("Error: SUT", entry.ToString()), + entry => Assert.Equal("Information: SUT", entry.ToString()), + entry => Assert.Equal("Warning: SUT", entry.ToString()), + entry => Assert.Equal("Information: Unique message for logger2.", entry.ToString())); } } } From d5e263467a353f6b04cc97642b9d1492665ed805 Mon Sep 17 00:00:00 2001 From: Michael Mortensen Date: Wed, 9 Apr 2025 21:10:34 +0200 Subject: [PATCH 4/6] :package: updated NuGet package definition --- .../Codebelt.Extensions.Xunit.Hosting/PackageReleaseNotes.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.nuget/Codebelt.Extensions.Xunit.Hosting/PackageReleaseNotes.txt b/.nuget/Codebelt.Extensions.Xunit.Hosting/PackageReleaseNotes.txt index f654cc1..5494919 100644 --- a/.nuget/Codebelt.Extensions.Xunit.Hosting/PackageReleaseNotes.txt +++ b/.nuget/Codebelt.Extensions.Xunit.Hosting/PackageReleaseNotes.txt @@ -16,13 +16,14 @@ Availability: .NET 9, .NET 8 and .NET Standard 2.0 # New Features - ADDED HostTest class in the Codebelt.Extensions.Xunit.Hosting namespace that represents the non-generic base class from where its generic equivalent should derive (e.g., MinimalHostTest{T}, HostTest{T}, etc.) - ADDED IGenericHostFixture interface in the Codebelt.Extensions.Xunit.Hosting namespace that provides a set of members for configuring the host -- ADDED GenericHostFixture class in the Codebelt.Extensions.Xunit.Hosting namespace that provides a default implementation of the IGenericHostFixture interface +- ADDED GenericHostFixture class in the Codebelt.Extensions.Xunit.Hosting namespace that provides a default implementation of the IGenericHostFixture interface (replaces the legacy HostFixture class) - ADDED GenericHostFixtureExtensions class in the Codebelt.Extensions.Xunit.Hosting namespace that consist of one extension method for the IGenericHostFixture interface: HasValidState - ADDED IMinimalHostFixture interface in the Codebelt.Extensions.Xunit.Hosting namespace that provides a set of members for configuring the host (minimal style) - ADDED MinimalHostFixture class in the Codebelt.Extensions.Xunit.Hosting namespace that provides a default implementation of the IMinimalHostFixture interface - ADDED MinimalHostFixtureExtensions class in the Codebelt.Extensions.Xunit.Hosting namespace that consist of one extension method for the IMinimalHostFixture interface: HasValidState - ADDED MinimalHostTest class in the Codebelt.Extensions.Xunit.Hosting namespace that represents the non-generic base class from where its generic equivalent should derive (e.g., MinimalWebHostTest, {T}, MinimalHostTest{T}, etc.) - ADDED MinimalHostTestFactory class in the Codebelt.Extensions.Xunit.Hosting namespace that provides a set of static methods for IHost unit testing (minimal style) +- EXTENDED ServiceCollectionExtensions class in the Codebelt.Extensions.Xunit.Hosting namespace with one new extension method for the IServiceCollection interface: An overload of AddXunitTestLogging that does not rely on the ITestOutputHelper interface   Version 9.1.3 Availability: .NET 9, .NET 8 and .NET Standard 2.0 From 8968854882a57eba2e3ba0becec2f42e813ae45a Mon Sep 17 00:00:00 2001 From: Michael Mortensen Date: Wed, 9 Apr 2025 21:10:40 +0200 Subject: [PATCH 5/6] :speech_balloon: updated community health pages --- CHANGELOG.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3fd6148..649f0bc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,13 +9,13 @@ For more details, please refer to `PackageReleaseNotes.txt` on a per assembly ba ## [10.0.0] - TBD -This major release introduces support for unit testing Minimal APIs and includes numerous breaking changes with valuable learnings from previous 9.0.x releases. These changes aim to ensure greater consistency across the `Codebelt.Extensions.Xunit.Hosting` and `Codebelt.Extensions.Xunit.Hosting.AspNetCore` namespaces. +This major release introduces support for unit testing Minimal APIs and includes numerous breaking changes with valuable learnings from previous 9.1.x releases. These changes aim to ensure greater consistency across the `Codebelt.Extensions.Xunit.Hosting` and `Codebelt.Extensions.Xunit.Hosting.AspNetCore` namespaces. ### Added - HostTest class in the Codebelt.Extensions.Xunit.Hosting namespace that represents the non-generic base class from where its generic equivalent should derive (e.g., MinimalHostTest{T}, HostTest{T}, etc.) - IGenericHostFixture interface in the Codebelt.Extensions.Xunit.Hosting namespace that provides a set of members for configuring the host -- GenericHostFixture class in the Codebelt.Extensions.Xunit.Hosting namespace that provides a default implementation of the IGenericHostFixture interface +- GenericHostFixture class in the Codebelt.Extensions.Xunit.Hosting namespace that provides a default implementation of the IGenericHostFixture interface (replaces the legacy HostFixture class) - GenericHostFixtureExtensions class in the Codebelt.Extensions.Xunit.Hosting namespace that consist of one extension method for the IGenericHostFixture interface: HasValidState - IMinimalHostFixture interface in the Codebelt.Extensions.Xunit.Hosting namespace that provides a set of members for configuring the host (minimal style) - MinimalHostFixture class in the Codebelt.Extensions.Xunit.Hosting namespace that provides a default implementation of the IMinimalHostFixture interface @@ -31,6 +31,8 @@ This major release introduces support for unit testing Minimal APIs and includes - MinimalHostFixtureExtensions class in the Codebelt.Extensions.Xunit.Hosting namespace that consist of one extension method for the IMinimalHostFixture interface: HasValidState - MinimalHostTest class in the Codebelt.Extensions.Xunit.Hosting namespace that represents the non-generic base class from where its generic equivalent should derive (e.g., MinimalWebHostTest, {T}, MinimalHostTest{T}, etc.) - MinimalHostTestFactory class in the Codebelt.Extensions.Xunit.Hosting namespace that provides a set of static methods for IHost unit testing (minimal style) +- ServiceCollectionExtensions class in the Codebelt.Extensions.Xunit.Hosting namespace received one new extension method for the IServiceCollection interface: An overload of AddXunitTestLogging that does not rely on the ITestOutputHelper interface + - This was done to help mitigate those scenarios where the [current design of xUnit v2 and ITestOutputHelper can cause deadlocks](https://github.com/xunit/xunit/discussions/2994), e.g. you can access the xUnit logger but nothing is logged to the test output ### Changed From ed3c8502938023062c9bdcbc9fffe05c35a1656a Mon Sep 17 00:00:00 2001 From: Michael Mortensen Date: Wed, 9 Apr 2025 21:16:16 +0200 Subject: [PATCH 6/6] :pencil2: fixed ambigious reference in cref attribute for AddXunitTestLogging --- src/Codebelt.Extensions.Xunit.Hosting/LoggerExtensions.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Codebelt.Extensions.Xunit.Hosting/LoggerExtensions.cs b/src/Codebelt.Extensions.Xunit.Hosting/LoggerExtensions.cs index 230526f..c85e32e 100644 --- a/src/Codebelt.Extensions.Xunit.Hosting/LoggerExtensions.cs +++ b/src/Codebelt.Extensions.Xunit.Hosting/LoggerExtensions.cs @@ -13,7 +13,7 @@ namespace Codebelt.Extensions.Xunit.Hosting public static class LoggerExtensions { /// - /// Returns the associated that is provided when settings up services from . + /// Returns the associated that is provided when settings up services from or related. /// /// The from which to retrieve the . /// The category name for messages produced by the -or- null for messages produced by all loggers. @@ -57,7 +57,7 @@ public static ITestStore GetTestStore(this ILogger logger, } /// - /// Returns the associated that is provided when settings up services from . + /// Returns the associated that is provided when settings up services from or related. /// /// The from which to retrieve the . /// Returns an implementation of with all logged entries expressed as .