Code coverage reports uncovered branch on method returning IAsyncEnumerable<T>
04:39 05 Mar 2026

I have the following function, which is basically a wrapper around something that makes a SQL call and reshapes the results:

        public async IAsyncEnumerable ExecuteTable(string sql, IDictionary? parameters, [EnumeratorCancellation] CancellationToken cancellationToken)
        {
            var res = await ExecuteSQL(sql, parameters, cancellationToken).ConfigureAwait(false);
            foreach (var row in res.Rows)
            {
                cancellationToken.ThrowIfCancellationRequested();
                yield return RowConverter.ConvertRow(res.Columns, row);
            }
        }

You might notice that the enumeration is not really async, but this is an implementation of an interface, and it has to return IAsyncEnumerable.

I have unit tests covering this. The tests do this:

  • Successful execution, with results.

  • Successful execution, with no results.

  • Cancellation in ExecuteSQL (which throws OperationCanceledException).

  • Cancellation in the loop.

  • Non-cancellation exception in ExecuteSQL.

This is the question: the code coverage tool reports a branch not covered, on the very last closed bracket (the one that ends the method). Why?

Of course, this isn't a big deal; I would be okay with ignoring the issue - except that there is another method, which is absolutely identical except that it's not generic and returns IAsyncEnumerable instead (with a RowConverter.CovertDynamic call instead of RowConverter.ConvertRow). That other method is exerted by unit tests in exactly the same way, and code coverage reports it as fully covered!

So, I'm curious as to what the difference could possibly be - why the compiler is apparently producing an unreachable branch, but only for generic and not for dynamic.

I've even tried debugging into the IL, but it's too difficult to follow. I've also created a non-generic and non-dynamic version just to see what happens; it reports as fully covered.

c# .net async-await code-coverage coverlet