Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ Availability: .NET 9, .NET 8, .NET 6 and .NET Standard 2.0

# New Features
- ADDED ServiceProviderExtensions class in the Cuemon.Extensions.Xunit.Hosting namespace that consist of one extension method for the IServiceProvider interface: GetRequiredScopedService
- EXTENDED ServiceCollectionExtensions class in the Cuemon.Extensions.Xunit.Hosting namespace with three new extension methods for the IServiceCollection interface: AddXunitTestOutputHelperAccessor, AddXunitTestOutputHelperAccessor{T} and an overload of AddXunitTestOutputHelperAccessor

Version 8.3.2
Availability: .NET 8, .NET 6 and .NET Standard 2.0
Expand Down
4 changes: 4 additions & 0 deletions .nuget/Cuemon.Extensions.Xunit/PackageReleaseNotes.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ Availability: .NET 9, .NET 8, .NET 6 and .NET Standard 2.0
# Improvements
- EXTENDED TestOutputHelperExtensions class in the Cuemon.Extensions.Xunit namespace with an additional overloaded method: WriteLines

# New Features
- ADDED ITestOutputHelperAccessor interface in the Cuemon.Extensions.Xunit namespace that provides access to the ITestOutputHelper instance
- ADDED TestOutputHelperAccessor class in the Cuemon.Extensions.Xunit namespace that provides a default implementation of the ITestOutputHelper interface

Version 8.3.2
Availability: .NET 8, .NET 6 and .NET Standard 2.0

Expand Down
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@ New features:
- FailureConverter class in the Cuemon.Extensions.Text.Json.Converters namespace to convert FailureConverter to JSON
- FailureConverter class in the Cuemon.Xml.Serialization.Converters namespace to convert FailureConverter to XML
- Support for System.Threading.Lock object that targets TFMs prior to .NET 9 (credits to Mark Cilia Vincenti, https://github.com/MarkCiliaVincenti/Backport.System.Threading.Lock)
- ITestOutputHelperAccessor interface in the Cuemon.Extensions.Xunit namespace that provides access to the ITestOutputHelper instance
- TestOutputHelperAccessor class in the Cuemon.Extensions.Xunit namespace that provides a default implementation of the ITestOutputHelper interface
- ServiceProviderExtensions class in the Cuemon.Extensions.Xunit.Hosting namespace that consist of one extension method for the IServiceProvider interface: GetRequiredScopedService
- ServiceCollectionExtensions class in the Cuemon.Extensions.Xunit.Hosting namespace was extended with three new extension methods for the IServiceCollection interface: AddXunitTestOutputHelperAccessor, AddXunitTestOutputHelperAccessor{T} and an overload of AddXunitTestOutputHelperAccessor

### Changed

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
</PropertyGroup>

<ItemGroup Condition="$(TargetFramework.StartsWith('net9'))">
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="9.0.0-preview.7.24406.2" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="9.0.0-rc.1.24452.1" />
</ItemGroup>

<ItemGroup Condition="$(TargetFramework.StartsWith('net8'))">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@
</PropertyGroup>

<ItemGroup Condition="$(TargetFramework.StartsWith('net9'))">
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="9.0.0-preview.7.24405.7" />
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="9.0.0-preview.7.24405.7" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="9.0.0-rc.1.24431.7" />
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="9.0.0-rc.1.24431.7" />
</ItemGroup>

<ItemGroup Condition="$(TargetFramework.StartsWith('net8'))">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
</PropertyGroup>

<ItemGroup Condition="$(TargetFramework.StartsWith('net9'))">
<PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.0-preview.7.24405.7" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.0-rc.1.24431.7" />
</ItemGroup>

<ItemGroup Condition="$(TargetFramework.StartsWith('net8'))">
Expand Down
2 changes: 1 addition & 1 deletion src/Cuemon.Extensions.Net/Cuemon.Extensions.Net.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
</PropertyGroup>

<ItemGroup Condition="$(TargetFramework.StartsWith('net9'))">
<PackageReference Include="Microsoft.Extensions.Http" Version="9.0.0-preview.7.24405.7" />
<PackageReference Include="Microsoft.Extensions.Http" Version="9.0.0-rc.1.24431.7" />
</ItemGroup>

<ItemGroup Condition="$(TargetFramework.StartsWith('net8'))">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
</ItemGroup>

<ItemGroup Condition="$(TargetFramework.StartsWith('net9'))">
<PackageReference Include="System.Text.Json" Version="9.0.0-preview.7.24405.7" />
<PackageReference Include="System.Text.Json" Version="9.0.0-rc.1.24431.7" />
</ItemGroup>

<ItemGroup Condition="$(TargetFramework.StartsWith('net8'))">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
</ItemGroup>

<ItemGroup Condition="$(TargetFramework.StartsWith('net9'))">
<PackageReference Include="Microsoft.AspNetCore.TestHost" Version="9.0.0-preview.7.24406.2" />
<PackageReference Include="Microsoft.AspNetCore.TestHost" Version="9.0.0-rc.1.24452.1" />
</ItemGroup>

<ItemGroup Condition="$(TargetFramework.StartsWith('net8'))">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,11 @@
</PropertyGroup>

<ItemGroup Condition="$(TargetFramework.StartsWith('net9'))">
<PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.0-preview.7.24405.7" />
<PackageReference Include="Microsoft.Extensions.Configuration" Version="9.0.0-preview.7.24405.7" />
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="9.0.0-preview.7.24405.7" />
<PackageReference Include="Microsoft.Extensions.Configuration.FileExtensions" Version="9.0.0-preview.7.24405.7" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="9.0.0-preview.7.24405.7" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.0-rc.1.24431.7" />
<PackageReference Include="Microsoft.Extensions.Configuration" Version="9.0.0-rc.1.24431.7" />
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="9.0.0-rc.1.24431.7" />
<PackageReference Include="Microsoft.Extensions.Configuration.FileExtensions" Version="9.0.0-rc.1.24431.7" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="9.0.0-rc.1.24431.7" />
</ItemGroup>

<ItemGroup Condition="$(TargetFramework.StartsWith('net8'))">
Expand Down
50 changes: 50 additions & 0 deletions src/Cuemon.Extensions.Xunit.Hosting/ServiceCollectionExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,5 +32,55 @@ public static IServiceCollection AddXunitTestLogging(this IServiceCollection ser
});
return services;
}

/// <summary>
/// Adds a unit test optimized implementation of output logging to the <paramref name="services"/> collection.
/// </summary>
/// <param name="services">The <see cref="IServiceCollection"/> to extend.</param>
/// <param name="accessor">The <see cref="ITestOutputHelperAccessor"/> that provides access to the output for the logging.</param>
/// <param name="minimumLevel">The <see cref="LogLevel"/> that specifies the minimum level to include for the logging.</param>
/// <returns>A reference to <paramref name="services" /> so that additional configuration calls can be chained.</returns>
/// <exception cref="ArgumentNullException">
/// <paramref name="services"/> cannot be null -or-
/// <paramref name="accessor"/> cannot be null.
/// </exception>
public static IServiceCollection AddXunitTestLogging(this IServiceCollection services, ITestOutputHelperAccessor accessor, LogLevel minimumLevel = LogLevel.Trace)
{
Validator.ThrowIfNull(services);
Validator.ThrowIfNull(accessor);
services.AddLogging(builder =>
{
builder.SetMinimumLevel(minimumLevel);
builder.AddProvider(new XunitTestLoggerProvider(accessor));
});
return services;
}

/// <summary>
/// Adds a default implementation of <see cref="ITestOutputHelperAccessor"/> to the <paramref name="services"/> collection.
/// </summary>
/// <param name="services">The <see cref="IServiceCollection"/> to extend.</param>
/// <returns>A reference to <paramref name="services" /> so that additional configuration calls can be chained.</returns>
public static IServiceCollection AddXunitTestOutputHelperAccessor(this IServiceCollection services)
{
services.AddXunitTestOutputHelperAccessor<TestOutputHelperAccessor>();
return services;
}

/// <summary>
/// Adds a specified implementation of <see cref="ITestOutputHelperAccessor"/> to the <paramref name="services"/> collection.
/// </summary>
/// <typeparam name="T">The type of the implementation of <see cref="ITestOutputHelperAccessor"/>.</typeparam>
/// <param name="services">The <see cref="IServiceCollection"/> to extend.</param>
/// <returns>A reference to <paramref name="services" /> so that additional configuration calls can be chained.</returns>
/// <exception cref="ArgumentNullException">
/// <paramref name="services"/> cannot be null.
/// </exception>
public static IServiceCollection AddXunitTestOutputHelperAccessor<T>(this IServiceCollection services) where T : class, ITestOutputHelperAccessor
{
Validator.ThrowIfNull(services);
services.AddSingleton<ITestOutputHelperAccessor, T>();
return services;
}
}
}
15 changes: 14 additions & 1 deletion src/Cuemon.Extensions.Xunit.Hosting/XunitTestLogger.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,33 @@ namespace Cuemon.Extensions.Xunit.Hosting
{
internal class XunitTestLogger : InMemoryTestStore<XunitTestLoggerEntry>, ILogger, IDisposable
{
private readonly ITestOutputHelperAccessor _accessor;
private readonly ITestOutputHelper _output;

public XunitTestLogger(ITestOutputHelper output)
{
_output = output;
}

public XunitTestLogger(ITestOutputHelperAccessor accessor)
{
_accessor = accessor;
}

public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)
{
var builder = new StringBuilder($"{logLevel}: {formatter(state, exception)}");
if (exception != null) { builder.AppendLine().Append(exception).AppendLine(); }

var message = builder.ToString();
_output.WriteLine(message);
if (_accessor != null)
{
_accessor.TestOutput.WriteLine(message);
}
else
{
_output.WriteLine(message);
}
Add(new XunitTestLoggerEntry(logLevel, eventId, message));
}

Expand Down
10 changes: 9 additions & 1 deletion src/Cuemon.Extensions.Xunit.Hosting/XunitTestLoggerProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,24 @@ namespace Cuemon.Extensions.Xunit.Hosting
internal class XunitTestLoggerProvider : ILoggerProvider
{
private readonly ConcurrentDictionary<string, XunitTestLogger> _loggers = new();
private readonly ITestOutputHelperAccessor _accessor;
private readonly ITestOutputHelper _output;

public XunitTestLoggerProvider(ITestOutputHelper output)
{
_output = output;
}

public XunitTestLoggerProvider(ITestOutputHelperAccessor accessor)
{
_accessor = accessor;
}

public ILogger CreateLogger(string categoryName)
{
return _loggers.GetOrAdd(categoryName, s => new XunitTestLogger(_output));
return _loggers.GetOrAdd(categoryName, _ => _accessor != null
? new XunitTestLogger(_accessor)
: new XunitTestLogger(_output));
}

public void Dispose()
Expand Down
16 changes: 16 additions & 0 deletions src/Cuemon.Extensions.Xunit/ITestOutputHelperAccessor.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
using Xunit.Abstractions;

namespace Cuemon.Extensions.Xunit
{
/// <summary>
/// Provides an interface for accessing the <see cref="ITestOutputHelper"/> instance.
/// </summary>
public interface ITestOutputHelperAccessor
{
/// <summary>
/// Gets or sets the <see cref="ITestOutputHelper"/> instance used for outputting test results.
/// </summary>
/// <value>The <see cref="ITestOutputHelper"/> instance.</value>
ITestOutputHelper TestOutput { get; set; }
}
}
32 changes: 32 additions & 0 deletions src/Cuemon.Extensions.Xunit/TestOutputHelperAccessor.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
using System.Threading;
using Xunit.Abstractions;

namespace Cuemon.Extensions.Xunit
{
/// <summary>
/// Provides a default implementation of the <see cref="ITestOutputHelper"/> interface.
/// </summary>
public class TestOutputHelperAccessor : ITestOutputHelperAccessor
{
private static readonly AsyncLocal<ITestOutputHelper> Current = new();

/// <summary>
/// Initializes a new instance of the <see cref="TestOutputHelperAccessor"/> class with the specified <see cref="ITestOutputHelper"/> instance.
/// </summary>
/// <param name="output">The <see cref="ITestOutputHelper"/> instance to be used for outputting test results.</param>
public TestOutputHelperAccessor(ITestOutputHelper output = null)
{
Current.Value = output;
}

/// <summary>
/// Gets or sets the <see cref="ITestOutputHelper"/> instance used for outputting test results.
/// </summary>
/// <value>The <see cref="ITestOutputHelper"/> instance.</value>
public ITestOutputHelper TestOutput
{
get => Current.Value;
set => Current.Value = value;
}
}
}
6 changes: 3 additions & 3 deletions test/Cuemon.Data.Tests/Cuemon.Data.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -26,15 +26,15 @@
</ItemGroup>

<ItemGroup Condition="$(TargetFramework.StartsWith('net9'))">
<PackageReference Include="Microsoft.Data.Sqlite" Version="9.0.0-preview.7.24405.3" />
<PackageReference Include="Microsoft.Data.Sqlite" Version="9.0.0-rc.1.24451.1" />
</ItemGroup>

<ItemGroup Condition="$(TargetFramework.StartsWith('net8'))">
<PackageReference Include="Microsoft.Data.Sqlite" Version="8.0.7" />
<PackageReference Include="Microsoft.Data.Sqlite" Version="8.0.8" />
</ItemGroup>

<ItemGroup Condition="$(TargetFramework.StartsWith('net6')) Or $(TargetFramework.StartsWith('net4'))">
<PackageReference Include="Microsoft.Data.Sqlite" Version="6.0.32" />
<PackageReference Include="Microsoft.Data.Sqlite" Version="6.0.33" />
</ItemGroup>

<ItemGroup Condition="$(TargetFramework.StartsWith('netstandard2'))">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Xunit;
using Xunit.Abstractions;
Expand All @@ -21,6 +22,8 @@ public AspNetCoreHostTestTest(AspNetCoreHostFixture hostFixture, ITestOutputHelp
{
_pipeline = hostFixture.Application;
_provider = hostFixture.ServiceProvider;

_provider.GetRequiredService<ITestOutputHelperAccessor>().TestOutput = output;
}

[Fact]
Expand All @@ -30,7 +33,7 @@ public async Task ShouldHaveResultOfBoolMiddlewareInBody()
var options = _provider.GetRequiredService<IOptions<BoolOptions>>();
var pipeline = _pipeline.Build();

Assert.Equal("Hello awesome developers!", context.Response.Body.ToEncodedString(o => o.LeaveOpen = true));
Assert.Equal("Hello awesome developers!", context!.Response.Body.ToEncodedString(o => o.LeaveOpen = true));

await pipeline(context);

Expand All @@ -44,8 +47,16 @@ public async Task ShouldHaveResultOfBoolMiddlewareInBody()
Assert.False(options.Value.F);
}

[Fact]
public void ShouldLogToXunitTestLogging()
{
var logger = _pipeline.ApplicationServices.GetRequiredService<ILogger<AspNetCoreHostTestTest>>();
logger.LogInformation("Hello from {0}", nameof(ShouldLogToXunitTestLogging));
}

public override void ConfigureApplication(IApplicationBuilder app)
{
app.ApplicationServices.GetRequiredService<ILogger<AspNetCoreHostTestTest>>().LogInformation(nameof(ConfigureApplication));
app.UseMiddleware<BoolMiddleware>();
}

Expand All @@ -58,6 +69,8 @@ public override void ConfigureServices(IServiceCollection services)
o.C = true;
o.E = true;
});
services.AddXunitTestOutputHelperAccessor();
services.AddXunitTestLogging(new TestOutputHelperAccessor(TestOutput));
}
}
}
}
2 changes: 1 addition & 1 deletion testenvironments.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
{
"name": "Docker-Ubuntu",
"type": "docker",
"dockerImage": "gimlichael/ubuntu-testrunner:net6.0.425-net8.0.401-9.0.100-preview.7.24407.12"
"dockerImage": "gimlichael/ubuntu-testrunner:net6.0.425-net8.0.401-9.0.100-rc.1.24452.12"
}
]
}