Skip to content

[Bug]: Instance MethodDataSource reuses enumeration instance for first yielded test case, breaking test isolation consistency #6361

Description

@iatsuta

Description

When using an instance-based MethodDataSource, TUnit creates a test class instance in order to execute the data source method during test case enumeration. That instance is then reused for execution of the test case corresponding to the first yielded value.

All subsequent test cases are executed on newly created test class instances.

As a result, exactly one test case (the first yielded one) shares state with the instance used during enumeration, while all other test cases do not. This creates an inconsistency in test execution behavior and allows side effects from data source generation to leak into a single test case.

This behavior can be observed when the data source method mutates instance state or produces observable side effects (logging, counters, collections, etc.).

Expected Behavior

The instance used to execute an instance-based MethodDataSource should not be reused for any test case execution.

Each test case generated by the data source should run on a freshly created test class instance, independent of the instance used during test case enumeration.

In other words, the lifecycle used for data source enumeration must be fully isolated from the lifecycle used for test execution, ensuring that no state or side effects from enumeration can affect any test case.

Actual Behavior

When an instance MethodDataSource is used, TUnit creates a test class instance to enumerate test cases. The same instance is then reused for executing the test case corresponding to the first yielded value.

All other test cases are executed on newly created test class instances.

This results in exactly one test case sharing state with the enumeration instance, while the rest do not, leading to inconsistent test execution behavior.

Steps to Reproduce

namespace TestProject1
{
    public sealed class TestClass1 : IDisposable
    {
        private static readonly TextWriter Writer = new StreamWriter("log.txt", true);

        private static int globalId;

        private readonly int id;

        public TestClass1()
        {
            this.id = Interlocked.Increment(ref globalId);

            Writer.WriteLine($"{this.id} | {this} TestClass instance created");
        }

        [Test]
        [MethodDataSource(nameof(GetCases), DeferEnumeration = true)]
        public async Task TestMethod3(string name, CancellationToken ct)
        {
            await Writer.WriteLineAsync($"{this.id} | Called TestMethod3 with name: {name}");
        }

        public async IAsyncEnumerable<string> GetCases()
        {
            await Writer.WriteLineAsync($"{this.id} | Called GetCases");

            yield return "Case1";
            yield return "Case2";
            yield return "Case3";
        }

        public void Dispose()
        {
            Writer.Flush();
        }
    }
}

TUnit Version

1.58.0

.NET Version

NET 10.0

Operating System

Windows

IDE / Test Runner

dotnet CLI (dotnet test / dotnet run)

Error Output / Stack Trace

-------------------
1 | TestProject1.TestClass1 TestClass instance created
1 | Called GetCases
3 | TestProject1.TestClass1 TestClass instance created
2 | TestProject1.TestClass1 TestClass instance created
3 | Called TestMethod3 with name: Case3
1 | Called TestMethod3 with name: Case1
2 | Called TestMethod3 with name: Case2

Additional Context

No response

IDE-Specific Issue?

  • I've confirmed this issue occurs when running via dotnet test or dotnet run, not just in my IDE

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions