Skip to content

Conversation

Copy link
Contributor

Copilot AI commented Aug 23, 2025

The source generator was not correctly detecting when a method returns async IAsyncEnumerable<T>, causing MethodDataSource attributes with async enumerable data sources to throw InvalidCastException at runtime.

Problem

When using MethodDataSource with an IAsyncEnumerable, TUnit would attempt to cast the compiler-generated async iterator (e.g., <GetAsyncTestData>d__0) directly to the test data type instead of iterating through the async enumerable and yielding individual values.

// This would fail with InvalidCastException
public static async IAsyncEnumerable<(int, string)> GetSimpleAsyncData()
{
    await Task.Delay(1);
    yield return (1, "first");
    yield return (2, "second");
    yield return (3, "third");
}

[Test]
[MethodDataSource(nameof(GetSimpleAsyncData))]
public async Task TestWithAsyncSimpleData(int id, string name) { ... }

Root Cause

The IsAsyncEnumerable() method in TestMetadataGenerator.cs only checked type.AllInterfaces for IAsyncEnumerable<T> implementations, but didn't check if the type itself was an IAsyncEnumerable<T>. For methods returning async IAsyncEnumerable<T>, the return type is directly IAsyncEnumerable<T>, not a type that implements it.

Solution

Updated the detection logic to check both:

  1. If the type itself is IAsyncEnumerable<T> (for direct returns)
  2. If the type implements IAsyncEnumerable<T> (for inherited implementations)

This ensures the source generator correctly generates the async enumerable iteration code:

await foreach (var item in result)
{
    yield return () => Task.FromResult(DataSourceHelpers.ToObjectArray(item));
}

Instead of incorrectly treating the async iterator as a single value.

Testing

Added comprehensive test cases covering:

  • IAsyncEnumerable<Func<CustomType>> (matching documentation examples)
  • IAsyncEnumerable<(int, string)> (tuple returns)
  • IAsyncEnumerable<object[]> (existing patterns)

All async enumerable tests now pass, and existing functionality remains unaffected.

Fixes #2990.


✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.

Copilot AI and others added 2 commits August 23, 2025 19:59
Co-authored-by: thomhurst <30480171+thomhurst@users.noreply.github.com>
Co-authored-by: thomhurst <30480171+thomhurst@users.noreply.github.com>
Copilot AI changed the title [WIP] MethodDataSources with IAsyncEnumerable<T> throw invalid cast exception Fix IAsyncEnumerable detection in MethodDataSource source generator Aug 23, 2025
Copilot AI requested a review from thomhurst August 23, 2025 20:08
Copilot finished work on behalf of thomhurst August 23, 2025 20:08
@thomhurst thomhurst marked this pull request as ready for review August 23, 2025 21:46
@thomhurst thomhurst merged commit 76e027a into main Aug 24, 2025
11 of 15 checks passed
@thomhurst thomhurst deleted the copilot/fix-2990 branch August 24, 2025 13:56
@claude claude bot mentioned this pull request Aug 30, 2025
1 task
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

MethodDataSources with IAsyncEnumerable<T> throw invalid cast exception

2 participants