< Summary

Information
Class: ArturRios.Util.Test.Mock.TestAsyncEnumerable<T>
Assembly: ArturRios.Util.Test
File(s): /home/runner/work/dotnet-test-util/dotnet-test-util/src/Mock/TestAsyncQueryProvider.cs
Line coverage
100%
Covered lines: 4
Uncovered lines: 0
Coverable lines: 4
Total lines: 74
Line coverage: 100%
Branch coverage
N/A
Covered branches: 0
Total branches: 0
Branch coverage: N/A
Method coverage
100%
Covered methods: 4
Fully covered methods: 4
Total methods: 4
Method coverage: 100%
Full method coverage: 100%

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%11100%
.ctor(...)100%11100%
GetAsyncEnumerator(...)100%11100%
System.Linq.IQueryable.get_Provider()100%11100%

File(s)

/home/runner/work/dotnet-test-util/dotnet-test-util/src/Mock/TestAsyncQueryProvider.cs

#LineLine coverage
 1using System.Linq.Expressions;
 2using System.Reflection;
 3using Microsoft.EntityFrameworkCore.Query;
 4
 5namespace ArturRios.Util.Test.Mock;
 6
 7/// <summary>
 8/// An <see cref="IAsyncQueryProvider"/> that executes queries synchronously over an in-memory source but
 9/// exposes them through the async pipeline, so EF Core's async operators (<c>ToListAsync</c>,
 10/// <c>FirstOrDefaultAsync</c>, <c>CountAsync</c>, …) work against a fake repository. Adapted from the standard
 11/// Entity Framework Core testing pattern.
 12/// </summary>
 13/// <typeparam name="TEntity">The element type of the queries created by this provider.</typeparam>
 14internal sealed class TestAsyncQueryProvider<TEntity>(IQueryProvider inner) : IAsyncQueryProvider
 15{
 16    public IQueryable CreateQuery(Expression expression) => new TestAsyncEnumerable<TEntity>(expression);
 17
 18    public IQueryable<TElement> CreateQuery<TElement>(Expression expression) => new TestAsyncEnumerable<TElement>(expres
 19
 20    public object? Execute(Expression expression) => inner.Execute(expression);
 21
 22    public TResult Execute<TResult>(Expression expression) => inner.Execute<TResult>(expression);
 23
 24    public TResult ExecuteAsync<TResult>(Expression expression, CancellationToken cancellationToken = default)
 25    {
 26        // EF Core's scalar async operators call ExecuteAsync with TResult = Task<T>; run the underlying
 27        // synchronous query and wrap the result in a completed Task<T>.
 28        var expectedResultType = typeof(TResult).GetGenericArguments()[0];
 29
 30        var executionResult = typeof(IQueryProvider)
 31            .GetMethod(nameof(IQueryProvider.Execute), 1, [typeof(Expression)])!
 32            .MakeGenericMethod(expectedResultType)
 33            .Invoke(this, [expression]);
 34
 35        return (TResult)typeof(Task)
 36            .GetMethod(nameof(Task.FromResult))!
 37            .MakeGenericMethod(expectedResultType)
 38            .Invoke(null, [executionResult])!;
 39    }
 40}
 41
 42/// <summary>
 43/// An <see cref="IQueryable{T}"/> whose provider is a <see cref="TestAsyncQueryProvider{TEntity}"/> and which
 44/// is itself an <see cref="IAsyncEnumerable{T}"/>, enabling both scalar async operators and
 45/// <c>await foreach</c> / <c>ToListAsync</c> over an in-memory source.
 46/// </summary>
 47/// <typeparam name="T">The element type.</typeparam>
 48internal sealed class TestAsyncEnumerable<T> : EnumerableQuery<T>, IAsyncEnumerable<T>, IQueryable<T>
 49{
 2050    public TestAsyncEnumerable(IEnumerable<T> enumerable) : base(enumerable) { }
 51
 252    public TestAsyncEnumerable(Expression expression) : base(expression) { }
 53
 54    public IAsyncEnumerator<T> GetAsyncEnumerator(CancellationToken cancellationToken = default) =>
 255        new TestAsyncEnumerator<T>(this.AsEnumerable().GetEnumerator());
 56
 657    IQueryProvider IQueryable.Provider => new TestAsyncQueryProvider<T>(this);
 58}
 59
 60/// <summary>An <see cref="IAsyncEnumerator{T}"/> that pulls from a synchronous enumerator.</summary>
 61/// <typeparam name="T">The element type.</typeparam>
 62internal sealed class TestAsyncEnumerator<T>(IEnumerator<T> inner) : IAsyncEnumerator<T>
 63{
 64    public T Current => inner.Current;
 65
 66    public ValueTask<bool> MoveNextAsync() => ValueTask.FromResult(inner.MoveNext());
 67
 68    public ValueTask DisposeAsync()
 69    {
 70        inner.Dispose();
 71
 72        return ValueTask.CompletedTask;
 73    }
 74}