简体   繁体   中英

The provider for the source 'IQueryable' doesn't implement 'IAsyncQueryProvider' when using MockQueryable

I'm using .NET 6 and I'm trying to mock the IQueryable return to implement IAsyncQueryProvider using the MockQueryable library, but I'm facing the same error with, and without the library:

Message: 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.

Stack Trace: EntityFrameworkQueryableExtensions.ExecuteAsync[TSource,TResult](MethodInfo operatorMethodInfo, IQueryable 1 source, Expression expression, CancellationToken cancellationToken) EntityFrameworkQueryableExtensions.ExecuteAsync[TSource,TResult](MethodInfo operatorMethodInfo, IQueryable 1 source, CancellationToken cancellationToken) EntityFrameworkQueryableExtensions.FirstOrDefaultAsync[TSource](IQueryable`1 source, CancellationToken cancellationToken) UserService.GetExistingUser(String externalId, CancellationToken cancellationToken) line 81 UserService.CreateAsync(UserDto dto, CancellationToken cancellationToken) line 21 UserServiceTests.CreateAsync_ShouldCreateUser_WhenUserDoesNotExist() line 46 --- End of stack trace from previous location ---

Maybe someone can help me spot something that might be an obvious error?

This is my Unit Test class:

public class UserServiceTests
{
    private readonly UserService _sut;
    private readonly Mock<IRepository<User>> _userRepositoryMock = new();

    public UserServiceTests()
    {
        _sut = new UserService(_userRepositoryMock.Object);
    }

    [Fact]
    public async Task CreateAsync_ShouldCreateUser_WhenUserDoesNotExist()
    {
        // Arrange
        var dto = new UserDto
        {
            EmailAddress = "createAsyncTest@email.com",
            ExternalId = "externalId2",
            Name = "Create Async"
        };

        var users = new List<User>();

        var mock = users.BuildMock();

        _userRepositoryMock.Setup(x => x.Find(y => y.ExternalId == dto.ExternalId)).Returns(mock).Verifiable();

        var user = UserMapper.GetUserExpression(null).Compile().Invoke(dto);

        _userRepositoryMock.Setup(x => x.InsertAsync(user, true, default)).Verifiable();

        // Act
        var res = await _sut.CreateAsync(dto);

        // Assert 
        Assert.NotNull(res);
        _userRepositoryMock.Verify();
    }
}

This is how I implement my generic repository:

public class Repository<TEntity> : IRepository<TEntity> where TEntity : class
{
    internal TestContext _context;
    internal DbSet<TEntity> _dbSet;

    public Repository(TestContext context)
    {
        _context = context;
        _context.Database.SetCommandTimeout(300);
        _dbSet = context.Set<TEntity>();
    }

    public IQueryable<TEntity> All() => _dbSet;

    public IQueryable<TEntity> Find() => All();

    public IQueryable<TEntity> Find(Expression<Func<TEntity, bool>> predicate)
    {
        return _dbSet.Where(predicate);
    }
}

And this is my UserService:

public class UserService : IUserService
{
    private readonly IRepository<User> _userRepository;

    public UserService(IRepository<User> userRepository)
    {
        _userRepository = userRepository;
    }

    public async Task<UserDto> CreateAsync(UserDto dto, CancellationToken cancellationToken = default)
    {
        var existingUser = await GetExistingUser(dto.ExternalId, cancellationToken);

        if (existingUser != null)
            throw new Exception("User already exists!");

        var user = UserMapper.GetUserExpression(null).Compile().Invoke(dto);

        await _userRepository.InsertAsync(user, true, cancellationToken);

        return UserMapper.GetUserDtoExpression().Compile().Invoke(user);
    }
    
    private async Task<User> GetExistingUser(string externalId, CancellationToken cancellationToken = default)
    {
        return await _userRepository
            .Find(x => x.ExternalId == externalId)
            .FirstOrDefaultAsync(cancellationToken);
    }
}

Predicate of type Expression<Func<User, bool>> passed to _userRepository.Find() is not the same expression you specified in _userRepositoryMock.Setup() (they have same logic and same externalId but they are different instances).

Instead of

_userRepositoryMock.Setup(x => x.Find(y => y.ExternalId == dto.ExternalId)).Returns(mock)

try this:

_userRepositoryMock.Setup(x => x.Find(It.IsAny<Expression<Func<User, bool>>>()).Returns(predicate => mock.Where(predicate))

That way, any expression passed to the.Find() method will be applied to mocked repository as is.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM