How to arrange a ReturnsCollection that is AsyncEnumerable

0 Answers 874 Views
Async Mocking
Edward
Top achievements
Rank 1
Edward asked on 05 Jun 2023, 02:49 PM

I have a test case to test my EF repository getallasync call as follows:

// Arrange
var systemsData = new List<SystemEntity>
{
new SystemEntity { SystemId = 1, Full_Year_Standings = true, Season_StartDate = DateTime.Now, Season_EndDate = DateTime.Now },
new SystemEntity { SystemId = 2, Full_Year_Standings = false, Season_StartDate = DateTime.Now, Season_EndDate = DateTime.Now },
new SystemEntity { SystemId = 3, Full_Year_Standings = true, Season_StartDate = DateTime.Now, Season_EndDate = DateTime.Now }
};

var mockContext = Mock.Create<BetCenterContext>(Behavior.Loose);
Mock.Arrange(() => mockContext.Set<SystemEntity>()).ReturnsCollection(systemsData);

var systemRepository = new SystemRepository(mockContext);

// Act
var result = await systemRepository.GetAllAsync();

 

Calling the following method:

 

public async Task<List<SystemEntity>> GetAllAsync()
{
var collToReturn = await _context.Set<SystemEntity>().ToListAsync();

return collToReturn;
}

The arranged mock returns a null result and throws InvalidOperationException - The source 'IQueryable' doesn't IAsyncEnumerable...'

How do I arrange the mock to return the correct value?

     
Edward
Top achievements
Rank 1
commented on 07 Jun 2023, 03:48 PM

Since the initial question I have learned a few different ways to convert my test data to a IAsyncEnumerable.  Among them writing a static method that yields the test data.  Also using the ToAsyncEnumerable method call in System.Linq.Async library.  Both give me the test object I want.

The problem now is that there doesn't seem to be any way to return the  IAsyncEnumerable test object in the Telerik Mock.Arrange methods. ReturnsCollection throws a compile error, as does Returns (which isn't a fit but I tried it anyways).

Any ideas?

Ivo
Telerik team
commented on 08 Jun 2023, 12:32 PM | edited

With little help from the extension methods like the following you can achieve the desired conversion, here is the code:

public static class IEnumeratorExtension
{
    public static async IAsyncEnumerable<T> ToAsyncEnumerable<T>(this IEnumerator<T> iterator)
    {
        while (iterator.MoveNext())
        {
            await Task.Yield();
            yield return iterator.Current;
        }
    }

    public static async IAsyncEnumerable<T> ToAsyncEnumerable<T>(this IEnumerator iterator)
    {
        while (iterator.MoveNext())
        {
            await Task.Yield();
            yield return (T)iterator.Current;
        }
    }
}
You will need aslo to change the arrangement in the test like that:
Mock.Arrange(() => mockContext.Set<SystemEntity>()).Returns(systemsData.GetEnumerator().ToAsyncEnumerable<SystemEntity>());
Per Ivar
Top achievements
Rank 1
commented on 12 Jun 2024, 08:39 AM

Hi
This does not seem to work.
Trying Ivo's suggestion, but that just fails to compile. 

        [Fact]
        public void ShouldReturnEntity()
        {
            var db = Mock.Create<MyModel.MyModel>();
            Mock.Arrange(() => db.SomeEntities).ReturnsCollection(someEntities);

            var entity = db.SomeEntities.SingleAsync(x => x.Id == 1);

            Assert.NotNull(entity);
        }


Having any async method call like SingleAsync above will give you and exception like 

System.InvalidOperationException
The provider for the source 'IQueryable' doesn't implement 'IAsyncQueryProvider'. Only providers that implement 'IAsyncQueryProvider' can be used for Entity Framework asynchronous operations.

This is a trivial example. 
One route is to mock SingleAsync call, but our code is more complex and mocking every async Linq call would be a nightmare (and not sure its possible with the Linq-queries we have).

Is there any way to get this to work ??
Adding a the simple example source as attachment.

 

Ivo
Telerik team
commented on 12 Jun 2024, 04:41 PM

Hello Per Ivar, I am afraid that JustMock will not be able to handle this exact scenario until the mock collection lacks IAsyncQueryProvider implementation. It is an excellent suggestion for improvement which we will take into consideration.

No answers yet. Maybe you can help?

Tags
Async Mocking
Asked by
Edward
Top achievements
Rank 1
Share this question
or