Repository navigation
[Bug]: DisposeAsync() ordering since 1.21.0 #5336
Description
Description
We have multilevel Nested property injection (at least 3) that all implement IAsyncInitializer and IAsyncDisposable. While upgrading to the latest version, we noticed that our test fixtures were failing during tearing down. We assumed that the DisposeAsync() were called in the reverse order of the InitialzeAsync(), but since v1.21.0 it seems this is not true anymore.
This breaks our fixture isolation that do do some setup/teardown of resources (mostly database & seeding) that have to be in that predictable order.
We would like to know if this a regression ? Otherwise can it be a feature, as it seems to be sane way to handle Nested initialization
Expected Behavior
DisposeAsync() to be called in reverse order of InitializeAsync() when nested properties initialization are used.
Actual Behavior
DisposeAsync() seems to be called in an arbitrary order.
Steps to Reproduce
using TUnit.Core.Interfaces;
namespace Playground;
public sealed class AppSeedFixture : IAsyncInitializer, IAsyncDisposable
{
[ClassDataSource<ContextFactoryFixture>(Shared = SharedType.PerTestSession)]
public required ContextFactoryFixture ContextFactoryFixture { get; init; }
public ValueTask DisposeAsync()
{
Console.WriteLine("Dispose {0}", GetType().Name);
return ValueTask.CompletedTask;
}
public Task InitializeAsync()
{
Console.WriteLine("Init {0}", GetType().Name);
return Task.CompletedTask;
}
}
public sealed class ContextFactoryFixture : IAsyncInitializer, IAsyncDisposable
{
public ValueTask DisposeAsync()
{
Console.WriteLine("Dispose {0}", GetType().Name);
return ValueTask.CompletedTask;
}
public Task InitializeAsync()
{
Console.WriteLine("Init {0}", GetType().Name);
return Task.CompletedTask;
}
}
public sealed class AppServiceFixture : IAsyncInitializer, IAsyncDisposable
{
[ClassDataSource<AppSeedFixture>(Shared = SharedType.PerTestSession)]
public required AppSeedFixture AppSeedFixture { get; init; }
public ValueTask DisposeAsync()
{
Console.WriteLine("Dispose {0}", GetType().Name);
return ValueTask.CompletedTask;
}
public Task InitializeAsync()
{
Console.WriteLine("Init {0}", GetType().Name);
return Task.CompletedTask;
}
}
public class SandboxTests
{
[ClassDataSource<AppServiceFixture>(Shared = SharedType.PerTestSession)]
public required AppServiceFixture AppServiceFixture { get; init; }
[Test]
public async Task Test1()
{
await Assert.That(0 == 0).IsTrue();
}
}outputs in 1.21.0
passed Test1 (4ms)
Standard output
Init ContextFactoryFixture
Init AppSeedFixture
Init AppServiceFixture
Dispose ContextFactoryFixture
Dispose AppSeedFixture
Dispose AppServiceFixture
output in 1.20.0
passed Test1 (5ms)
Standard output
Init ContextFactoryFixture
Init AppSeedFixture
Init AppServiceFixture
Dispose AppServiceFixture
Dispose AppSeedFixture
Dispose ContextFactoryFixture
Error output
TUnit Version
1.21.24
.NET Version
.Net 10
Operating System
macOS
IDE / Test Runner
dotnet CLI (dotnet test / dotnet run)
Error Output / Stack Trace
Additional Context
No response
IDE-Specific Issue?
- I've confirmed this issue occurs when running via
dotnet testordotnet run, not just in my IDE
Activity
[-][Bug]: DisposeAsync() ordering since 1.20.0[/-][+][Bug]: DisposeAsync() ordering since 1.21.0[/+]jeremad commentedon Apr 1, 2026
https://github.com/thomhurst/TUnit/pull/5213/files#diff-809088b394fd911cb5c4584464dd6fbfef50d4455c67e10f545d038c77853e9eR117
In my understanding, changing
OrderByDescendingtoOrderByAscendingshould fix the issuethomhurst commentedon Apr 1, 2026
Should be fixed in the latest!
jeremad commentedon Apr 2, 2026
Fixed indeed! thanks