Description
Note: this issue batches 5 small findings from a TUnit migration (mix of bugs + ergonomic asks). Filing as one issue for context; happy to split into separate issues if preferred.
Building on #5525 (comment) — the migration is well underway (several hundred tests on TUnit 1.36.0, more landing per release). Overall the framework has been a pleasure — [Matrix], [ParallelLimiter<T>], [ClassDataSource<T>(SharedType.PerClass)] with required init, Assert.Multiple, and fluent Throws<T>() / ThrowsNothing() have all delivered. The TUnit 1.36 TUnit.OpenTelemetry + TestWebApplicationFactory work lines up cleanly for our upcoming HTTP-endpoint tests.
Along the way we hit five small rough edges. Each is verified against 1.36.0 with a minimal repro.
| # |
Finding |
Severity |
| 1 |
.IsNotNull() on Task produces a misleading compile error suggesting JsonElement |
Low — confusing diagnostic |
| 2 |
IsEqualTo on arrays uses reference equality; failure message renders as "System.UInt32[] ≠ System.UInt32[]" |
Medium — silent footgun + unhelpful error |
| 3 |
IsNotSameReferenceAs(task) fails to infer generic argument — #1573 handles object receivers; typed-reference receivers (e.g. Task) still fail |
Low |
| 4 |
[MatrixDataSource] should be implicit when parameters carry [Matrix] (related to #4765's class-level cascade) |
Low — ergonomics |
| 5 |
TUnit0015 has no Roslyn code-fix to auto-insert CancellationToken (#4767 demoted to warning; escalates to error under TreatWarningsAsErrors) |
Low — ergonomics |
Expected Behavior
1. Assert.That(task).IsNotNull() should either (a) check the reference of the Task itself, or (b) fail to compile with a diagnostic that is actually relevant to Task — not one pointing at JsonElement.
2. Assert.That(array).IsEqualTo(array) should compare elements (matching xUnit, NUnit, FluentAssertions). When it fails, the error should format the contents ([2, 5, 7] vs [2, 5, 8]), not render both sides as "System.UInt32[]".
3. Assert.That(task).IsNotSameReferenceAs(otherTask) should infer the generic argument from the receiver, as #1572 / #1573 intended.
4. A test method with [Matrix] parameters should either (a) behave as if [MatrixDataSource] were applied implicitly, or (b) have a Roslyn code-fix that inserts [MatrixDataSource] on one keystroke when TUnit0049 fires.
5. TUnit0015 should ship with a code-fix that offers "Add CancellationToken parameter" (with variants for ThrowIfCancellationRequested, discard, or unused).
Actual Behavior
1. Compile error CS1929 points the user at JsonElementAssertionExtensions.IsNotNull(IAssertionSource<JsonElement>). Workaround: cast the Task to object first.
2. Assertion fails at runtime with the unhelpful message "Expected to be equal to System.UInt32[] but received System.UInt32[]". Arrays don't override ToString, so the diff is invisible. Workaround: element-by-element loop.
3. Compile error CS0411 — "type arguments cannot be inferred from the usage". #1573's fix works for object receivers; Task-typed receivers still trip CS0411. Workaround: explicit <Task> generic annotation.
4. Build error TUnit0049 — [MatrixDataSourceAttribute] is required if using [Matrix] values on your parameters. The user must add a second attribute manually; no code-fix.
5. TUnit0015 fires correctly but the fix is entirely manual: append the parameter, then either use it or discard it to silence IDE0060 / CA1801.
Steps to Reproduce
All 5 repros were verified in a fresh console project targeting TUnit 1.36.0 on .NET 10.0.202 (Windows).
1. Task.IsNotNull — misleading diagnostic:
[Test]
public async Task Issue1(CancellationToken ct)
{
_ = ct;
Task t = Task.CompletedTask;
await Assert.That(t).IsNotNull(); // CS1929 — diagnostic suggests JsonElement
}
Workaround: await Assert.That((object)t).IsNotNull();
2. Array IsEqualTo — reference equality + uninformative failure:
[Test]
public async Task Issue2(CancellationToken ct)
{
_ = ct;
uint[] actual = [2u, 5u, 7u];
uint[] expected = [2u, 5u, 7u];
await Assert.That(actual).IsEqualTo(expected); // fails: "System.UInt32[] ≠ System.UInt32[]"
}
Workaround: compare Length + iterate elementwise.
3. IsNotSameReferenceAs — generic inference fails:
[Test]
public async Task Issue3(CancellationToken ct)
{
_ = ct;
Task a = Task.CompletedTask;
Task b = Task.FromResult(true);
await Assert.That(a).IsNotSameReferenceAs(b); // CS0411
}
Workaround: await Assert.That(a).IsNotSameReferenceAs<Task>(b);
4. [Matrix] without [MatrixDataSource]:
[Test]
public async Task Issue4(
[Matrix(true, false)] bool flag,
CancellationToken ct)
{
_ = ct;
await Assert.That(flag || !flag).IsTrue(); // TUnit0049 at build time
}
Workaround: add [MatrixDataSource] on the method.
5. TUnit0015 fires as warning — no code-fix, manual edit required:
[Test]
[Timeout(1000)]
public async Task Issue5()
{
await Task.Yield(); // TUnit0015 — no code-fix; manual edit required
}
Workaround: add CancellationToken cancellationToken parameter, then _ = cancellationToken; or cancellationToken.ThrowIfCancellationRequested();.
TUnit Version
1.36.0
.NET Version
10.0.202
Operating System
Windows
IDE / Test Runner
dotnet CLI (dotnet test / dotnet run)
Error Output / Stack Trace
Additional Context
Suggested directions
1. Add IsNotNull / IsNull extension methods on AsyncDelegateAssertion that check the reference (not the delegate's awaited result), or ship an analyzer that catches the pattern and suggests the right form.
2. Either default IsEqualTo on T[] / IEnumerable<T> to SequenceEqual (matches xUnit, NUnit, FluentAssertions), or keep reference semantics but ship a dedicated IsSequenceEqualTo. Orthogonally: when both sides render as the same string, fall back to formatting the contents so the diff is visible.
3. The TValue parameter can be inferred from the receiver (IAssertionSource<TValue>). Changing the second-arg type from object? to TValue would let the compiler resolve without the annotation — matching how IsSameReferenceAs behaves for object receivers after #1573.
4. Two viable fixes: (a) treat [Matrix] on any parameter as implicit [MatrixDataSource] on the method, or (b) ship a Roslyn code-fix with TUnit0049 that inserts [MatrixDataSource] on one keystroke. Option (a) is cleaner long-term; option (b) is a faster shim. Adjacent to #4765 but scoped to per-method rather than class-level cascade.
5. Ship a code-fix with TUnit0015 offering three intents: Add CancellationToken parameter, Add CancellationToken parameter and ThrowIfCancellationRequested, Add CancellationToken parameter as discard. Meziantou.Analyzer ships code-fixes alongside most of its analyzers — similar pattern here would be welcome.
Configuration
No AOT / trimming involved. Standard TUnit NuGet package, default parallelism. None of the 5 findings are IDE-specific — all reproduced via dotnet run from the CLI in a fresh dotnet new console project.
Closing
Happy to test any fixes against the live migration — we re-run the full TUnit suite on every TUnit release, so reproduction of anything you fix is one dotnet test away on our side.
Thanks for the framework and the pace.
IDE-Specific Issue?
Description
Note: this issue batches 5 small findings from a TUnit migration (mix of bugs + ergonomic asks). Filing as one issue for context; happy to split into separate issues if preferred.
Building on #5525 (comment) — the migration is well underway (several hundred tests on TUnit 1.36.0, more landing per release). Overall the framework has been a pleasure —
[Matrix],[ParallelLimiter<T>],[ClassDataSource<T>(SharedType.PerClass)]withrequired init,Assert.Multiple, and fluentThrows<T>()/ThrowsNothing()have all delivered. The TUnit 1.36TUnit.OpenTelemetry+TestWebApplicationFactorywork lines up cleanly for our upcoming HTTP-endpoint tests.Along the way we hit five small rough edges. Each is verified against 1.36.0 with a minimal repro.
.IsNotNull()onTaskproduces a misleading compile error suggestingJsonElementIsEqualToon arrays uses reference equality; failure message renders as"System.UInt32[] ≠ System.UInt32[]"IsNotSameReferenceAs(task)fails to infer generic argument — #1573 handles object receivers; typed-reference receivers (e.g. Task) still fail[MatrixDataSource]should be implicit when parameters carry[Matrix](related to #4765's class-level cascade)CancellationToken(#4767 demoted to warning; escalates to error under TreatWarningsAsErrors)Expected Behavior
1.
Assert.That(task).IsNotNull()should either (a) check the reference of theTaskitself, or (b) fail to compile with a diagnostic that is actually relevant toTask— not one pointing atJsonElement.2.
Assert.That(array).IsEqualTo(array)should compare elements (matching xUnit, NUnit, FluentAssertions). When it fails, the error should format the contents ([2, 5, 7]vs[2, 5, 8]), not render both sides as"System.UInt32[]".3.
Assert.That(task).IsNotSameReferenceAs(otherTask)should infer the generic argument from the receiver, as #1572 / #1573 intended.4. A test method with
[Matrix]parameters should either (a) behave as if[MatrixDataSource]were applied implicitly, or (b) have a Roslyn code-fix that inserts[MatrixDataSource]on one keystroke when TUnit0049 fires.5. TUnit0015 should ship with a code-fix that offers "Add
CancellationTokenparameter" (with variants forThrowIfCancellationRequested, discard, or unused).Actual Behavior
1. Compile error
CS1929points the user atJsonElementAssertionExtensions.IsNotNull(IAssertionSource<JsonElement>). Workaround: cast the Task toobjectfirst.2. Assertion fails at runtime with the unhelpful message
"Expected to be equal to System.UInt32[] but received System.UInt32[]". Arrays don't overrideToString, so the diff is invisible. Workaround: element-by-element loop.3. Compile error
CS0411— "type arguments cannot be inferred from the usage". #1573's fix works for object receivers; Task-typed receivers still trip CS0411. Workaround: explicit<Task>generic annotation.4. Build error
TUnit0049—[MatrixDataSourceAttribute] is required if using [Matrix] values on your parameters. The user must add a second attribute manually; no code-fix.5. TUnit0015 fires correctly but the fix is entirely manual: append the parameter, then either use it or discard it to silence IDE0060 / CA1801.
Steps to Reproduce
All 5 repros were verified in a fresh console project targeting TUnit 1.36.0 on .NET 10.0.202 (Windows).
1. Task.IsNotNull — misleading diagnostic:
Workaround:
await Assert.That((object)t).IsNotNull();2. Array IsEqualTo — reference equality + uninformative failure:
Workaround: compare
Length+ iterate elementwise.3. IsNotSameReferenceAs — generic inference fails:
Workaround:
await Assert.That(a).IsNotSameReferenceAs<Task>(b);4. [Matrix] without [MatrixDataSource]:
Workaround: add
[MatrixDataSource]on the method.5. TUnit0015 fires as warning — no code-fix, manual edit required:
Workaround: add
CancellationToken cancellationTokenparameter, then_ = cancellationToken;orcancellationToken.ThrowIfCancellationRequested();.TUnit Version
1.36.0
.NET Version
10.0.202
Operating System
Windows
IDE / Test Runner
dotnet CLI (dotnet test / dotnet run)
Error Output / Stack Trace
Additional Context
Suggested directions
1. Add
IsNotNull/IsNullextension methods onAsyncDelegateAssertionthat check the reference (not the delegate's awaited result), or ship an analyzer that catches the pattern and suggests the right form.2. Either default
IsEqualToonT[]/IEnumerable<T>toSequenceEqual(matches xUnit, NUnit, FluentAssertions), or keep reference semantics but ship a dedicatedIsSequenceEqualTo. Orthogonally: when both sides render as the same string, fall back to formatting the contents so the diff is visible.3. The
TValueparameter can be inferred from the receiver (IAssertionSource<TValue>). Changing the second-arg type fromobject?toTValuewould let the compiler resolve without the annotation — matching howIsSameReferenceAsbehaves forobjectreceivers after #1573.4. Two viable fixes: (a) treat
[Matrix]on any parameter as implicit[MatrixDataSource]on the method, or (b) ship a Roslyn code-fix with TUnit0049 that inserts[MatrixDataSource]on one keystroke. Option (a) is cleaner long-term; option (b) is a faster shim. Adjacent to #4765 but scoped to per-method rather than class-level cascade.5. Ship a code-fix with TUnit0015 offering three intents: Add
CancellationTokenparameter, AddCancellationTokenparameter andThrowIfCancellationRequested, AddCancellationTokenparameter as discard. Meziantou.Analyzer ships code-fixes alongside most of its analyzers — similar pattern here would be welcome.Configuration
No AOT / trimming involved. Standard TUnit NuGet package, default parallelism. None of the 5 findings are IDE-specific — all reproduced via
dotnet runfrom the CLI in a freshdotnet new consoleproject.Closing
Happy to test any fixes against the live migration — we re-run the full TUnit suite on every TUnit release, so reproduction of anything you fix is one
dotnet testaway on our side.Thanks for the framework and the pace.
IDE-Specific Issue?
dotnet testordotnet run, not just in my IDE