Unit-Testing, DbContext mocking Async throw InvalidOperationException: The source IQueryable doesn't implement IDbAsyncEnumerable

Viewed 145

Follow instructions from Testing with a mocking framework, I can mock Async function for DBSet like CountAsync..., but exception "InvalidOperationException: The source IQueryable doesn't implement IDbAsyncEnumerable<OrderSummary>" is throwed when I write to return with anonymous or custom type. Any solution to perform it?

My mocking

public class DbContextBuilderFixture : IDisposable
{
    protected readonly Mock<DbSet<Item>> MockDbSetItem  = new Mock<DbSet<Item>>();
    public DbContextBuilderFixture()
    {

    }

    public ShopingContext Build()
    {
        var mockContext = new Mock<ShopingContext>();

        mockContext.Setup(t=>t.Items).Returns(() => MockDbSetItem.Object);

        return mockContext.Object;
    }


    public Mock<DbSet<Item>> WithItems(List<Item> items)
    {
        var data = stockTakes.AsQueryable();

        MockDbSetItem.As<IDbAsyncEnumerable<Item>>()
            .Setup(m => m.GetAsyncEnumerator())
            .Returns(new DbAsyncEnumeratorHelper<Item>(data.GetEnumerator()));

        MockDbSetItem.As<IQueryable<Item>>()
            .Setup(m => m.Provider)
            .Returns(new DbAsyncQueryProviderHelper<Item>(data.Provider));
        
        MockDbSetItem.As<IQueryable<Item>>().Setup(m => m.Expression).Returns(data.Expression);
        MockDbSetItem.As<IQueryable<Item>>().Setup(m => m.ElementType).Returns(data.ElementType);
        MockDbSetItem.As<IQueryable<Item>>().Setup(m => m.GetEnumerator()).Returns(data.GetEnumerator());

        return MockDbSetItem;
    }

    public void Dispose()
    {
        MockDbSetItem?.Invocations.Clear();
        MockDbSetItem?.Reset();
    }
}

My code (Entity Framework 6.0):

Public Taks<Dictionary<int, OrderSummary> GetOrderSummaryAsync()
{
    return await Db.Items
    .GroupBy(
        i => i.OrderId,
        i => new 
        {
            i.SubTotal,
            i.Quantity
        },
        (key, g) => new OrderSummary
        {
            Id = key,
            Total = g.Sum(i => i.SubTotal),
            TotalCount = g.Sum(i => i.Quantity)
        })
    .ToDictionaryAsync(i => i.Id);
}

My Unitest

[Fact]
public void GetOrderSumamry_ShouldCorrectTotalCount()
{
    //arrange
    var listItem = new List<Item>
    {
        BuilderItem.Default()
            .WithId(1)
            .WithOrderId(1)
            .WithCreatedDate(new DateTime(2021, 10, 20))
            .WithSubTotal(1000)
            .WithQuantity(200)
            .Build(),

        BuilderItem.Default()
            .WithId(2)
            .WithOrderId(1)
            .WithCreatedDate(new DateTime(2021, 10, 21))
            .WithSubTotal(1000)
            .WithQuantity(200)
            .Build()
    };
    
    DbContext.WithItems(listItem);
   
    var itemService = new ItemService(DbContext.Build());

    //action
    /*
    exception "InvalidOperationException: The source IQueryable doesn't 
    implement IDbAsyncEnumerable<OrderSummary>"
    */
    var orderSummaryDict = itemService.GetOrderSummaryAsync().Result;

    //actual
    orderSummaryDict[1].TotalCount.Should().Be(400);
}

0 Answers
Related