0

I'm new to unit test and Moq. I have a repository class to retrieve users. Here is the repository class code:

public class UserRepository : IUserRepository
    {
        private readonly UserManager<UserEntity> _userManager;
        private readonly IMapper _mapper;

        public UserRepository(
            UserManager<UserEntity> userManager,
            IMapper mapper)
        {
            _userManager = userManager;
            _mapper = mapper;
        }

        public async Task<IEnumerable<User>> GetUsersAsync(
            PagingOptions pagingOptions)
        {
            IQueryable<UserEntity> query = _userManager.Users;
            //    query = searchOptions.Apply(query);
            //    query = sortOptions.Apply(query);

            var size = await query.CountAsync();

            var items = await query
                .Skip(pagingOptions.Offset.Value)
                .Take(pagingOptions.Limit.Value)
                .ProjectTo<User>(_mapper.ConfigurationProvider)
                .ToArrayAsync();

            return items;
        }
}

This works fine when I run the application normally as shown in the below image.

working code

But When I run the same code using x-unit unit test project, I get the below error when executing var size = await query.CountAsync();:

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

Here is my unit test code:

public class UserRepositoryTests
    {
        [Fact]
        public async void GetUsers_AtleastOne_ReturnOneOrMore()
        {
            // Arrange
            var connectionStringBuilder =
                new SqliteConnectionStringBuilder { DataSource = ":memory:" };
            var connection = new SqliteConnection(connectionStringBuilder.ToString());

            var options = new DbContextOptionsBuilder<GallaContext>()
                .UseSqlite(connection)
                .Options;

            var mockHttpContextAccessor = new Mock<IHttpContextAccessor>();
            var mockUserManager = new Mock<UserManager<UserEntity>>(new Mock<IUserStore<UserEntity>>().Object,
                    new Mock<IOptions<IdentityOptions>>().Object,
                    new Mock<IPasswordHasher<UserEntity>>().Object,
                    new IUserValidator<UserEntity>[0],
                    new IPasswordValidator<UserEntity>[0],
                    new Mock<ILookupNormalizer>().Object,
                    new Mock<IdentityErrorDescriber>().Object,
                    new Mock<IServiceProvider>().Object,
                    new Mock<ILogger<UserManager<UserEntity>>>().Object);
            var autoMapper = new MapperConfiguration(mc => mc.AddProfile(new MappingProfile())).CreateMapper();

            var pagingOptions = new PagingOptions
            {
                Limit = 25,
                Offset = 0
            };

            using (var context = new GallaContext(options, mockHttpContextAccessor.Object))
            {
                context.Database.OpenConnection();
                context.Database.EnsureCreated();

                var userRepository = new UserRepository(mockUserManager.Object, autoMapper);

                // Act
                var users = await userRepository.GetUsersAsync(pagingOptions);

                // Assert
                users.Should().HaveCountGreaterOrEqualTo(1);
            }
        }
    }

Am I mocking the UserManager wrongly? Also my context class has data to be added in database in OnModelCreating method. So the context has data when context.Database.EnsureCreated(); is called in unit test. Here is the screenshot of the error image while running unit test

unit test exception

Thanks,

Abdul

fingers10
  • 2,651
  • 2
  • 18
  • 46
  • Did you consider to use In-Memory provider for DbContext? Then you don't need to mock user manager – Fabio Jul 06 '19 at 23:45
  • You mean `options.UseInMemoryDatabase("dbname")`? But with this I'm not able to verify Foreign Key Violation, Unique Constraints. That's why I moved to `Sqlite` – fingers10 Jul 07 '19 at 00:49
  • 1
    Then use Sqlite, my point still stands - don't mock user manager then. – Fabio Jul 07 '19 at 00:50
  • Then without mocking how can I pass user manager instance? – fingers10 Jul 07 '19 at 00:53
  • Why you are creating sqlite DbContext if you don't use it in test? – Fabio Jul 07 '19 at 00:56
  • I'm using `Sqlite` in my tests. Notice that I'm passing `options` in the `GallaContext` instance. – fingers10 Jul 07 '19 at 01:02
  • Yes, but you not passing created context anywhere... – Fabio Jul 07 '19 at 01:04
  • But `context.Database.OpenConnection();context.Database.EnsureCreated();` is.required to seed master user data. At least that needs to be returned right? Or how to handle this Please assist – fingers10 Jul 07 '19 at 01:07
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/196092/discussion-between-abdul-rahman-and-fabio). – fingers10 Jul 07 '19 at 01:08
  • Check not accepted answer from the duplicated question [https://stackoverflow.com/a/49174248/1565525](https://stackoverflow.com/a/49174248/1565525) and last comment on the answer if you want to use DbContext in tests. – Fabio Jul 07 '19 at 01:33
  • @Fabio After analyzing this yesterday, I found where I went wrong and what the actual issue is. The issue is not with the `UserManager`. The issue is with EFCore `Async` operations. Now how to add my answer? I'm not able to find a option to add the correct answer. Please assist – fingers10 Jul 15 '19 at 09:32
  • This question can be marked as duplicate of https://stackoverflow.com/q/51023223/10851213. I resolved this by following the answer in this link https://stackoverflow.com/a/52800602/10851213 – fingers10 Jul 18 '19 at 15:02

0 Answers0