How can I return IQueryable object from a mock object?

.net-core asynchronous c# entity-framework-core nsubstitute

Question

I am trying to tell a method GetAll() on a mocked object _portalUserRepositoryMock to return an object of type IQueryable<TEntity>. I know it is of this type because the method in the class to be tested returns this type.

I've not been able to come up with a solution. I saw this post, but had errors trying to include the library into my project. Something about the version of Microsoft.EntityFrameworkCore - which led to more issues.

What I did to get this error was:

_portalUserRepositoryMock = Substitute.For<IPortalUserRepository>();
_portalUserRepositoryMock.GetAll().Returns(fakeQueryablePUser.AsQueryable());

The class under test uses the repository like this:

var portal = await _portalUserRepository.GetAll().Include(p => 
p.Portal).Where(p => p.UserId == user.Id && p.Portal.PortalType == 
dto.PortalType).FirstOrDefaultAsync();

and the GetAll() method is:

        public IQueryable<TEntity> GetAll()
    {
        try
        {
            return DbContext.Set<TEntity>().AsNoTracking();
        }
        catch (Exception ex)
        {
            throw ex;
        }
    }

I get this error:

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

I reckon I'm getting this error because of the FirstOrDefaultAsync() that is being used. Just have no idea on how to resolve it.

Edit: I have now been able to add the MockQueryable library to my test project (by using version 1.0.4 and not the latest 1.1.0 ). I have followed the steps, as shown below:

        var fakePortalUser = new PortalUser()
        {
            PortalId = new Guid()
        };

        var fakeQueryablePUser = new List<PortalUser>
        {
            fakePortalUser
        }.AsQueryable().BuildMock();

Last step is now to use GetQueryable(). Which I try to use here:

_portalUserRepositoryMock.GetAll().GetQueryable().Returns(fakeQueryablePUser);

But I get the red squigly line under theGetQueryable() method call. So code won't compile.

1
2
9/5/2019 3:21:57 PM

Accepted Answer

The initial issue is as you suspected with FirstOrDefaultAsync. That extension expects the queryable to also have a IAsyncQueryProvider in order to match async EF which the mock wont be by default.

Remove .GetQueryable() as you do not need it. That member came from one of the samples to demonstrate how to use the mock library.

Also make sure the fake data has data in it that will match the predicate in the Where.

.Where(p => p.UserId == user.Id && p.Portal.PortalType == dto.PortalType)

FirstOrDefault will return null if there are no elements to enumerate.

And if the data does not satisfy the filter you will get back null by default.

2
9/5/2019 6:23:15 PM

Popular Answer

You dont, IQueryable<T> is an implementation detail handled by Entity Framework / Core. Unless your business logic actually creates an implementation of IQueryable<T> then you want to return a mock stub object.

i.e (Note that this uses the library Moq to mock the objects as im not sure what you're using and the implementation may vary.)

_mockedEntityQuery = new Mock<IQueryable<T>>();
_portalUserRepositoryMock = Substitute.For<IPortalUserRepository>();
_portalUserRepositoryMock.GetAll().Returns(_mockedEntityQuery.Object);

If you return an instance of IQueryable<T> you would also be testing that implementation which will have already been done by the EF unit tests. Unit tests should only test the code that is within the scope of the unit.



Related Questions





Related

Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow