diff --git a/.nuget/Codebelt.Extensions.Xunit.Hosting/PackageReleaseNotes.txt b/.nuget/Codebelt.Extensions.Xunit.Hosting/PackageReleaseNotes.txt
index 28a10d6..0e9f5af 100644
--- a/.nuget/Codebelt.Extensions.Xunit.Hosting/PackageReleaseNotes.txt
+++ b/.nuget/Codebelt.Extensions.Xunit.Hosting/PackageReleaseNotes.txt
@@ -10,6 +10,8 @@ Availability: .NET 9, .NET 8 and .NET Standard 2.0
# New Features
- EXTENDED HostFixture class in the Codebelt.Extensions.Xunit.Hosting namespace to enable ValidateOnBuild and ValidateScopes when TFM is .NET 9 (or greater) and started the Host for consistency with AspNetCoreHostFixture
+- EXTENDED IHostFixture interface in the Codebelt.Extensions.Xunit.Hosting namespace with two new methods: Dispose and DisposeAsync
+- EXTENDED HostFixture class in the Codebelt.Extensions.Xunit.Hosting namespace with three new methods: InitializeAsync, OnDisposeManagedResourcesAsync, Dispose and DisposeAsync
Version 8.4.1
Availability: .NET 8, .NET 6 and .NET Standard 2.0
diff --git a/.nuget/Codebelt.Extensions.Xunit/PackageReleaseNotes.txt b/.nuget/Codebelt.Extensions.Xunit/PackageReleaseNotes.txt
index a98ff08..4ed08f4 100644
--- a/.nuget/Codebelt.Extensions.Xunit/PackageReleaseNotes.txt
+++ b/.nuget/Codebelt.Extensions.Xunit/PackageReleaseNotes.txt
@@ -5,6 +5,10 @@ Availability: .NET 9, .NET 8 and .NET Standard 2.0
- CHANGED Dependencies to latest and greatest with respect to TFMs
- REMOVED Support for TFM .NET 6 (LTS)
+# New Features
+- EXTENDED ITest interface in the Codebelt.Extensions.Xunit namespace with one new method: DisposeAsync
+- EXTENDED Test class in the Codebelt.Extensions.Xunit namespace with three new methods: InitializeAsync, OnDisposeManagedResourcesAsync and DisposeAsync
+
Version 8.4.0
Availability: .NET 8, .NET 6 and .NET Standard 2.0
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 84db6e8..6220636 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -14,6 +14,10 @@ This major release is first and foremost focused on ironing out any wrinkles tha
### Added
- StringExtensions class in the Codebelt.Extensions.Xunit namespace with one extension method (TFM netstandard2.0) for the String class: ReplaceLineEndings
+- ITest interface in the Codebelt.Extensions.Xunit namespace was extended with one new method: DisposeAsync
+- Test class in the Codebelt.Extensions.Xunit namespace was extended with three new methods: InitializeAsync, OnDisposeManagedResourcesAsync and DisposeAsync
+- IHostFixture interface in the Codebelt.Extensions.Xunit.Hosting namespace was extended with two new methods: Dispose and DisposeAsync
+- HostFixture class in the Codebelt.Extensions.Xunit.Hosting namespace was extended with three new methods: InitializeAsync, OnDisposeManagedResourcesAsync, Dispose and DisposeAsync
### Changed
diff --git a/src/Codebelt.Extensions.Xunit.Hosting/Codebelt.Extensions.Xunit.Hosting.csproj b/src/Codebelt.Extensions.Xunit.Hosting/Codebelt.Extensions.Xunit.Hosting.csproj
index 491973a..fd2dd45 100644
--- a/src/Codebelt.Extensions.Xunit.Hosting/Codebelt.Extensions.Xunit.Hosting.csproj
+++ b/src/Codebelt.Extensions.Xunit.Hosting/Codebelt.Extensions.Xunit.Hosting.csproj
@@ -34,10 +34,6 @@
-
-
-
-
diff --git a/src/Codebelt.Extensions.Xunit.Hosting/GlobalSuppressions.cs b/src/Codebelt.Extensions.Xunit.Hosting/GlobalSuppressions.cs
index b73414a..34a41d4 100644
--- a/src/Codebelt.Extensions.Xunit.Hosting/GlobalSuppressions.cs
+++ b/src/Codebelt.Extensions.Xunit.Hosting/GlobalSuppressions.cs
@@ -6,3 +6,4 @@
using System.Diagnostics.CodeAnalysis;
[assembly: SuppressMessage("Major Code Smell", "S3881:\"IDisposable\" should be implemented correctly", Justification = "This is an implementation of the IDisposable interface tailored to avoid wrong implementations.", Scope = "type", Target = "~T:Codebelt.Extensions.Xunit.Hosting.HostFixture")]
+[assembly: SuppressMessage("Major Code Smell", "S3971:\"GC.SuppressFinalize\" should not be called", Justification = "False-Positive due to IAsyncDisposable living side-by-side with IDisposable.", Scope = "member", Target = "~M:Codebelt.Extensions.Xunit.Hosting.HostFixture.DisposeAsync~System.Threading.Tasks.ValueTask")]
diff --git a/src/Codebelt.Extensions.Xunit.Hosting/HostFixture.cs b/src/Codebelt.Extensions.Xunit.Hosting/HostFixture.cs
index af3485a..340bfeb 100644
--- a/src/Codebelt.Extensions.Xunit.Hosting/HostFixture.cs
+++ b/src/Codebelt.Extensions.Xunit.Hosting/HostFixture.cs
@@ -4,6 +4,7 @@
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
+using Xunit;
namespace Codebelt.Extensions.Xunit.Hosting
{
@@ -11,7 +12,7 @@ namespace Codebelt.Extensions.Xunit.Hosting
/// Provides a default implementation of the interface.
///
///
- public class HostFixture : IDisposable, IHostFixture
+ public class HostFixture : IHostFixture, IAsyncLifetime
{
private readonly object _lock = new();
@@ -174,6 +175,34 @@ protected virtual void OnDisposeManagedResources()
Host?.Dispose();
}
+ ///
+ /// Called when this object is being disposed by .
+ ///
+#if NET8_0_OR_GREATER
+ protected virtual async ValueTask OnDisposeManagedResourcesAsync()
+ {
+ if (ServiceProvider is ServiceProvider sp)
+ {
+ await sp.DisposeAsync();
+ }
+
+ if (Host is IAsyncDisposable asyncDisposable)
+ {
+ await asyncDisposable.DisposeAsync();
+ }
+ else
+ {
+ Host?.Dispose();
+ }
+ }
+#else
+ protected virtual ValueTask OnDisposeManagedResourcesAsync()
+ {
+ OnDisposeManagedResources();
+ return default;
+ }
+#endif
+
///
/// Called when this object is being disposed by either or and is false.
///
@@ -208,5 +237,31 @@ protected void Dispose(bool disposing)
Disposed = true;
}
}
+
+ ///
+ /// Asynchronously releases the resources used by the .
+ ///
+ /// A that represents the asynchronous dispose operation.
+ /// https://learn.microsoft.com/en-us/dotnet/standard/garbage-collection/implementing-disposeasync#the-disposeasync-method
+ public async ValueTask DisposeAsync()
+ {
+ await OnDisposeManagedResourcesAsync().ConfigureAwait(false);
+ Dispose(false);
+ GC.SuppressFinalize(this);
+ }
+
+ ///
+ /// Called immediately after the class has been created, before it is used.
+ ///
+ /// A that represents the asynchronous operation.
+ public virtual Task InitializeAsync()
+ {
+ return Task.CompletedTask;
+ }
+
+ Task IAsyncLifetime.DisposeAsync()
+ {
+ return DisposeAsync().AsTask();
+ }
}
}
diff --git a/src/Codebelt.Extensions.Xunit.Hosting/IHostFixture.cs b/src/Codebelt.Extensions.Xunit.Hosting/IHostFixture.cs
index c332b0d..515d746 100644
--- a/src/Codebelt.Extensions.Xunit.Hosting/IHostFixture.cs
+++ b/src/Codebelt.Extensions.Xunit.Hosting/IHostFixture.cs
@@ -5,26 +5,37 @@
namespace Codebelt.Extensions.Xunit.Hosting
{
- ///
- /// Provides a way to use Microsoft Dependency Injection in unit tests.
- ///
- ///
- public interface IHostFixture : IServiceTest, IHostTest, IConfigurationTest, IHostingEnvironmentTest
- {
#if NETSTANDARD2_0_OR_GREATER
+ public partial interface IHostFixture
+ {
///
/// Gets or sets the delegate that adds configuration and environment information to a .
///
/// The delegate that adds configuration and environment information to a .
Action ConfigureCallback { get; set; }
+ }
#else
+ public partial interface IHostFixture
+ {
///
/// Gets or sets the delegate that adds configuration and environment information to a .
///
/// The delegate that adds configuration and environment information to a .
Action ConfigureCallback { get; set; }
+ }
#endif
+ ///
+ /// Provides a way to use Microsoft Dependency Injection in unit tests.
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public partial interface IHostFixture : IServiceTest, IHostTest, IConfigurationTest, IHostingEnvironmentTest, IDisposable, IAsyncDisposable
+ {
///
/// Gets or sets the delegate that adds services to the container.
///
diff --git a/src/Codebelt.Extensions.Xunit/Codebelt.Extensions.Xunit.csproj b/src/Codebelt.Extensions.Xunit/Codebelt.Extensions.Xunit.csproj
index 45e0624..caec1bb 100644
--- a/src/Codebelt.Extensions.Xunit/Codebelt.Extensions.Xunit.csproj
+++ b/src/Codebelt.Extensions.Xunit/Codebelt.Extensions.Xunit.csproj
@@ -11,9 +11,14 @@
-
+
+
+
+
+
+
diff --git a/src/Codebelt.Extensions.Xunit/GlobalSuppressions.cs b/src/Codebelt.Extensions.Xunit/GlobalSuppressions.cs
index fbdf1b5..8023644 100644
--- a/src/Codebelt.Extensions.Xunit/GlobalSuppressions.cs
+++ b/src/Codebelt.Extensions.Xunit/GlobalSuppressions.cs
@@ -6,3 +6,4 @@
using System.Diagnostics.CodeAnalysis;
[assembly: SuppressMessage("Major Code Smell", "S3881:\"IDisposable\" should be implemented correctly", Justification = "This is a base class implementation of the IDisposable interface tailored to avoid wrong implementations.", Scope = "type", Target = "~T:Codebelt.Extensions.Xunit.Test")]
+[assembly: SuppressMessage("Major Code Smell", "S3971:\"GC.SuppressFinalize\" should not be called", Justification = "False-Positive due to IAsyncDisposable living side-by-side with IDisposable.", Scope = "member", Target = "~M:Codebelt.Extensions.Xunit.Test.DisposeAsync~System.Threading.Tasks.ValueTask")]
diff --git a/src/Codebelt.Extensions.Xunit/ITest.cs b/src/Codebelt.Extensions.Xunit/ITest.cs
index 5d09798..b08272a 100644
--- a/src/Codebelt.Extensions.Xunit/ITest.cs
+++ b/src/Codebelt.Extensions.Xunit/ITest.cs
@@ -6,7 +6,7 @@ namespace Codebelt.Extensions.Xunit
/// Represents the members needed for vanilla testing.
///
///
- public interface ITest : IDisposable
+ public interface ITest : IDisposable, IAsyncDisposable
{
///
/// Gets the type of caller for this instance. Default is .
diff --git a/src/Codebelt.Extensions.Xunit/Test.cs b/src/Codebelt.Extensions.Xunit/Test.cs
index e8ce291..6994126 100644
--- a/src/Codebelt.Extensions.Xunit/Test.cs
+++ b/src/Codebelt.Extensions.Xunit/Test.cs
@@ -1,6 +1,8 @@
using System;
using System.Linq;
using System.Text.RegularExpressions;
+using System.Threading.Tasks;
+using Xunit;
using Xunit.Abstractions;
namespace Codebelt.Extensions.Xunit
@@ -9,8 +11,10 @@ namespace Codebelt.Extensions.Xunit
/// Represents the base class from which all implementations of unit testing should derive.
///
///
- public abstract class Test : ITest
+ public abstract class Test : ITest, IAsyncLifetime
{
+ private readonly object _lock = new();
+
///
/// Provides a way, with wildcard support, to determine if matches .
///
@@ -82,6 +86,14 @@ protected virtual void OnDisposeManagedResources()
{
}
+ ///
+ /// Called when this object is being disposed by .
+ ///
+ protected virtual ValueTask OnDisposeManagedResourcesAsync()
+ {
+ return default;
+ }
+
///
/// Called when this object is being disposed by either or and is false.
///
@@ -105,12 +117,42 @@ public void Dispose()
protected void Dispose(bool disposing)
{
if (Disposed) { return; }
- if (disposing)
+ lock (_lock)
{
- OnDisposeManagedResources();
+ if (Disposed) { return; }
+ if (disposing)
+ {
+ OnDisposeManagedResources();
+ }
+ OnDisposeUnmanagedResources();
+ Disposed = true;
}
- OnDisposeUnmanagedResources();
- Disposed = true;
+ }
+
+ ///
+ /// Asynchronously releases the resources used by the .
+ ///
+ /// A that represents the asynchronous dispose operation.
+ /// https://learn.microsoft.com/en-us/dotnet/standard/garbage-collection/implementing-disposeasync#the-disposeasync-method
+ public async ValueTask DisposeAsync()
+ {
+ await OnDisposeManagedResourcesAsync().ConfigureAwait(false);
+ Dispose(false);
+ GC.SuppressFinalize(this);
+ }
+
+ ///
+ /// Called immediately after the class has been created, before it is used.
+ ///
+ /// A that represents the asynchronous operation.
+ public virtual Task InitializeAsync()
+ {
+ return Task.CompletedTask;
+ }
+
+ Task IAsyncLifetime.DisposeAsync()
+ {
+ return DisposeAsync().AsTask();
}
}
}
diff --git a/test/Codebelt.Extensions.Xunit.Hosting.AspNetCore.Tests/AspNetCoreHostFixtureTest.cs b/test/Codebelt.Extensions.Xunit.Hosting.AspNetCore.Tests/AspNetCoreHostFixtureTest.cs
new file mode 100644
index 0000000..243fa39
--- /dev/null
+++ b/test/Codebelt.Extensions.Xunit.Hosting.AspNetCore.Tests/AspNetCoreHostFixtureTest.cs
@@ -0,0 +1,65 @@
+using System;
+using Codebelt.Extensions.Xunit.Hosting.AspNetCore.Assets;
+using Microsoft.AspNetCore.Builder;
+using Xunit;
+using Xunit.Abstractions;
+
+namespace Codebelt.Extensions.Xunit.Hosting.AspNetCore
+{
+ public class AspNetCoreHostFixtureTest : Test
+ {
+ public AspNetCoreHostFixtureTest(ITestOutputHelper output) : base(output)
+ {
+ }
+
+ [Fact]
+ public void ConfigureHost_NullHostTest_ThrowsArgumentNullException()
+ {
+ // Arrange
+ var fixture = new AspNetCoreHostFixture();
+
+ // Act & Assert
+ Assert.Throws(() => fixture.ConfigureHost(null));
+ }
+
+ [Fact]
+ public void ConfigureHost_InvalidHostTestType_ThrowsArgumentOutOfRangeException()
+ {
+ // Arrange
+ var fixture = new AspNetCoreHostFixture();
+ var invalidHostTest = new InvalidHostTest(fixture);
+
+ // Act & Assert
+ Assert.Throws(() => fixture.ConfigureHost(invalidHostTest));
+ }
+
+ [Fact]
+ public void ConfigureApplicationCallback_SetAndGet_ReturnsCorrectValue()
+ {
+ // Arrange
+ var fixture = new AspNetCoreHostFixture();
+ Action callback = app => { };
+
+ // Act
+ fixture.ConfigureApplicationCallback = callback;
+
+ // Assert
+ Assert.Equal(callback, fixture.ConfigureApplicationCallback);
+ }
+
+ [Fact]
+ public void ConfigureHost_ValidHostTest_ConfiguresHostCorrectly()
+ {
+ // Arrange
+ var fixture = new AspNetCoreHostFixture();
+ var hostTest = new ValidHostTest(fixture);
+
+ // Act
+ fixture.ConfigureHost(hostTest);
+
+ // Assert
+ Assert.NotNull(fixture.Host);
+ Assert.NotNull(fixture.Application);
+ }
+ }
+}
diff --git a/test/Codebelt.Extensions.Xunit.Hosting.AspNetCore.Tests/Assets/InvalidHostTest.cs b/test/Codebelt.Extensions.Xunit.Hosting.AspNetCore.Tests/Assets/InvalidHostTest.cs
new file mode 100644
index 0000000..c2ba881
--- /dev/null
+++ b/test/Codebelt.Extensions.Xunit.Hosting.AspNetCore.Tests/Assets/InvalidHostTest.cs
@@ -0,0 +1,11 @@
+using Xunit;
+
+namespace Codebelt.Extensions.Xunit.Hosting.AspNetCore.Assets
+{
+ public class InvalidHostTest : Test, IClassFixture where T : class, IHostFixture
+ {
+ public InvalidHostTest(T hostFixture)
+ {
+ }
+ }
+}
diff --git a/test/Codebelt.Extensions.Xunit.Hosting.AspNetCore.Tests/Assets/ValidHostTest.cs b/test/Codebelt.Extensions.Xunit.Hosting.AspNetCore.Tests/Assets/ValidHostTest.cs
new file mode 100644
index 0000000..544cc78
--- /dev/null
+++ b/test/Codebelt.Extensions.Xunit.Hosting.AspNetCore.Tests/Assets/ValidHostTest.cs
@@ -0,0 +1,22 @@
+using Microsoft.AspNetCore.Builder;
+using Microsoft.Extensions.DependencyInjection;
+
+namespace Codebelt.Extensions.Xunit.Hosting.AspNetCore.Assets
+{
+ public class ValidHostTest : AspNetCoreHostTest
+ {
+ public ValidHostTest(AspNetCoreHostFixture hostFixture) : base(hostFixture)
+ {
+ }
+
+ public override void ConfigureServices(IServiceCollection services)
+ {
+
+ }
+
+ public override void ConfigureApplication(IApplicationBuilder app)
+ {
+
+ }
+ }
+}
diff --git a/test/Codebelt.Extensions.Xunit.Hosting.AspNetCore.Tests/Codebelt.Extensions.Xunit.Hosting.AspNetCore.Tests.csproj b/test/Codebelt.Extensions.Xunit.Hosting.AspNetCore.Tests/Codebelt.Extensions.Xunit.Hosting.AspNetCore.Tests.csproj
index 7f9809d..b7f7f06 100644
--- a/test/Codebelt.Extensions.Xunit.Hosting.AspNetCore.Tests/Codebelt.Extensions.Xunit.Hosting.AspNetCore.Tests.csproj
+++ b/test/Codebelt.Extensions.Xunit.Hosting.AspNetCore.Tests/Codebelt.Extensions.Xunit.Hosting.AspNetCore.Tests.csproj
@@ -6,8 +6,8 @@
-
-
+
+
diff --git a/test/Codebelt.Extensions.Xunit.Hosting.Tests/Assets/InvalidHostTest.cs b/test/Codebelt.Extensions.Xunit.Hosting.Tests/Assets/InvalidHostTest.cs
new file mode 100644
index 0000000..9bc2e92
--- /dev/null
+++ b/test/Codebelt.Extensions.Xunit.Hosting.Tests/Assets/InvalidHostTest.cs
@@ -0,0 +1,11 @@
+using Xunit;
+
+namespace Codebelt.Extensions.Xunit.Hosting.Assets
+{
+ public class InvalidHostTest : Test, IClassFixture where T : class, IHostFixture
+ {
+ public InvalidHostTest(T hostFixture)
+ {
+ }
+ }
+}
diff --git a/test/Codebelt.Extensions.Xunit.Hosting.Tests/Assets/ValidHostTest.cs b/test/Codebelt.Extensions.Xunit.Hosting.Tests/Assets/ValidHostTest.cs
new file mode 100644
index 0000000..4a5d268
--- /dev/null
+++ b/test/Codebelt.Extensions.Xunit.Hosting.Tests/Assets/ValidHostTest.cs
@@ -0,0 +1,18 @@
+using System;
+using Microsoft.Extensions.DependencyInjection;
+using Xunit.Abstractions;
+
+namespace Codebelt.Extensions.Xunit.Hosting.Assets
+{
+ public class ValidHostTest : HostTest
+ {
+ public ValidHostTest(HostFixture hostFixture) : base(hostFixture)
+ {
+ }
+
+ public override void ConfigureServices(IServiceCollection services)
+ {
+
+ }
+ }
+}
diff --git a/test/Codebelt.Extensions.Xunit.Hosting.Tests/Codebelt.Extensions.Xunit.Hosting.Tests.csproj b/test/Codebelt.Extensions.Xunit.Hosting.Tests/Codebelt.Extensions.Xunit.Hosting.Tests.csproj
index 6c49c08..2ad30d4 100644
--- a/test/Codebelt.Extensions.Xunit.Hosting.Tests/Codebelt.Extensions.Xunit.Hosting.Tests.csproj
+++ b/test/Codebelt.Extensions.Xunit.Hosting.Tests/Codebelt.Extensions.Xunit.Hosting.Tests.csproj
@@ -15,7 +15,7 @@
-
+
diff --git a/test/Codebelt.Extensions.Xunit.Hosting.Tests/HostFixtureTest.cs b/test/Codebelt.Extensions.Xunit.Hosting.Tests/HostFixtureTest.cs
new file mode 100644
index 0000000..d139fd2
--- /dev/null
+++ b/test/Codebelt.Extensions.Xunit.Hosting.Tests/HostFixtureTest.cs
@@ -0,0 +1,58 @@
+using System;
+using System.Threading.Tasks;
+using Codebelt.Extensions.Xunit.Hosting.Assets;
+using Xunit;
+using Xunit.Abstractions;
+
+namespace Codebelt.Extensions.Xunit.Hosting
+{
+ public class HostFixtureTest : Test
+ {
+ private readonly HostFixture _hostFixture;
+
+ public HostFixtureTest(ITestOutputHelper output) : base(output)
+ {
+ _hostFixture = new HostFixture();
+ }
+
+ [Fact]
+ public void ConfigureHost_ShouldThrowArgumentNullException_WhenHostTestIsNull()
+ {
+ Assert.Throws(() => _hostFixture.ConfigureHost(null));
+ }
+
+ [Fact]
+ public void ConfigureHost_ShouldThrowArgumentOutOfRangeException_WhenHostTestIsNotAssignableFromHostTest()
+ {
+ var invalidHostTest = new InvalidHostTest(new HostFixture());
+ Assert.Throws(() => _hostFixture.ConfigureHost(invalidHostTest));
+ }
+
+ [Fact]
+ public void ConfigureHost_ShouldConfigureHostSuccessfully()
+ {
+ var validHostTest = new ValidHostTest(_hostFixture);
+
+ _hostFixture.ConfigureHost(validHostTest);
+
+ Assert.NotNull(_hostFixture.Host);
+ Assert.NotNull(_hostFixture.ServiceProvider);
+ Assert.NotNull(_hostFixture.Configuration);
+ Assert.NotNull(_hostFixture.HostingEnvironment);
+ }
+
+ [Fact]
+ public void Dispose_ShouldDisposeResources()
+ {
+ _hostFixture.Dispose();
+ Assert.True(_hostFixture.Disposed);
+ }
+
+ [Fact]
+ public async Task DisposeAsync_ShouldDisposeResourcesAsync()
+ {
+ await _hostFixture.DisposeAsync();
+ Assert.True(_hostFixture.Disposed);
+ }
+ }
+}
diff --git a/test/Codebelt.Extensions.Xunit.Tests/Assets/AsyncDisposable.cs b/test/Codebelt.Extensions.Xunit.Tests/Assets/AsyncDisposable.cs
new file mode 100644
index 0000000..6adddb0
--- /dev/null
+++ b/test/Codebelt.Extensions.Xunit.Tests/Assets/AsyncDisposable.cs
@@ -0,0 +1,38 @@
+using System;
+using System.IO;
+using System.Threading.Tasks;
+
+namespace Codebelt.Extensions.Xunit.Assets
+{
+ public class AsyncDisposable : Test
+ {
+ IDisposable _disposableResource = new MemoryStream();
+#if NET8_0_OR_GREATER
+ IAsyncDisposable _asyncDisposableResource = new MemoryStream();
+#else
+ IAsyncDisposable _asyncDisposableResource = new WemoryStream();
+#endif
+
+ protected override void OnDisposeManagedResources()
+ {
+ _disposableResource?.Dispose();
+ _disposableResource = null;
+ DisposableResourceCalled = true;
+ }
+
+ public bool DisposableResourceCalled { get; private set; }
+
+ protected override async ValueTask OnDisposeManagedResourcesAsync()
+ {
+ if (_asyncDisposableResource is not null)
+ {
+ await _asyncDisposableResource.DisposeAsync().ConfigureAwait(false);
+ AsyncDisposableResourceCalled = true;
+ }
+ _asyncDisposableResource = null;
+ OnDisposeManagedResources();
+ }
+
+ public bool AsyncDisposableResourceCalled { get; private set; }
+ }
+}
diff --git a/test/Codebelt.Extensions.Xunit.Tests/Assets/WemoryStream.cs b/test/Codebelt.Extensions.Xunit.Tests/Assets/WemoryStream.cs
new file mode 100644
index 0000000..eb02c50
--- /dev/null
+++ b/test/Codebelt.Extensions.Xunit.Tests/Assets/WemoryStream.cs
@@ -0,0 +1,20 @@
+using System;
+using System.IO;
+using System.Threading.Tasks;
+
+namespace Codebelt.Extensions.Xunit.Assets
+{
+ public class WemoryStream : MemoryStream, IAsyncDisposable
+ {
+ protected virtual async ValueTask DisposeAsyncCore()
+ {
+ Dispose(false);
+ }
+
+ public async ValueTask DisposeAsync()
+ {
+ await DisposeAsyncCore();
+ GC.SuppressFinalize(this);
+ }
+ }
+}
diff --git a/test/Codebelt.Extensions.Xunit.Tests/DisposableTest.cs b/test/Codebelt.Extensions.Xunit.Tests/DisposableTest.cs
index e0646e9..22e2265 100644
--- a/test/Codebelt.Extensions.Xunit.Tests/DisposableTest.cs
+++ b/test/Codebelt.Extensions.Xunit.Tests/DisposableTest.cs
@@ -1,4 +1,5 @@
using System;
+using System.Threading.Tasks;
using Codebelt.Extensions.Xunit.Assets;
using Xunit;
using Xunit.Abstractions;
@@ -11,7 +12,39 @@ public DisposableTest(ITestOutputHelper output) : base(output)
{
}
+#if NET8_0_OR_GREATER
+ [Fact]
+ public async Task AsyncDisposable_VerifyThatAssetIsBeingDisposed()
+ {
+ AsyncDisposable ad = new AsyncDisposable();
+ await using (ad.ConfigureAwait(false))
+ {
+ Assert.NotNull(ad);
+ Assert.False(ad.DisposableResourceCalled);
+ Assert.False(ad.AsyncDisposableResourceCalled);
+ }
+ Assert.NotNull(ad);
+ Assert.True(ad.DisposableResourceCalled);
+ Assert.True(ad.AsyncDisposableResourceCalled);
+ }
+#else
+ [Fact]
+ public async Task AsyncDisposable_VerifyThatAssetIsBeingDisposed()
+ {
+ AsyncDisposable ad = new AsyncDisposable();
+
+ Assert.NotNull(ad);
+ Assert.False(ad.DisposableResourceCalled);
+ Assert.False(ad.AsyncDisposableResourceCalled);
+
+ await ad.DisposeAsync().ConfigureAwait(false);
+
+ Assert.NotNull(ad);
+ Assert.True(ad.DisposableResourceCalled);
+ Assert.True(ad.AsyncDisposableResourceCalled);
+ }
+#endif
[Fact]
public void ManagedDisposable_VerifyThatAssetIsBeingDisposed()
diff --git a/test/Codebelt.Extensions.Xunit.Tests/TestTest.cs b/test/Codebelt.Extensions.Xunit.Tests/TestTest.cs
index 333b708..53edd5c 100644
--- a/test/Codebelt.Extensions.Xunit.Tests/TestTest.cs
+++ b/test/Codebelt.Extensions.Xunit.Tests/TestTest.cs
@@ -1,4 +1,5 @@
using System;
+using System.Threading.Tasks;
using Xunit;
using Xunit.Abstractions;
using Xunit.Sdk;
@@ -9,11 +10,30 @@ public class TestTest : Test
{
private const string ExpectedStringValue = "AllIsGood";
private bool _onDisposeManagedResourcesCalled;
+ private bool _initializeAsyncCalled;
public TestTest(ITestOutputHelper output) : base(output)
{
}
+ public override Task InitializeAsync()
+ {
+ _initializeAsyncCalled = true;
+ return Task.CompletedTask;
+ }
+
+ protected override ValueTask OnDisposeManagedResourcesAsync()
+ {
+ TestOutput.WriteLine($"{nameof(IAsyncLifetime.DisposeAsync)} was called.");
+ return default;
+ }
+
+ [Fact]
+ public void Test_InitializeAsyncCalled_ShouldBeTrue()
+ {
+ Assert.True(_initializeAsyncCalled);
+ }
+
[Fact]
public void Test_ShouldHaveTestOutput()
{