romantitov / MockQueryable

Mocking Entity Framework Core operations such ToListAsync, FirstOrDefaultAsync etc
MIT License
785 stars 76 forks source link

FindAsync is not working properly #33

Closed risogolo closed 4 years ago

risogolo commented 4 years ago

I saw it, similar has been discussed already, but it does still not work on my end

I have

var countries = CountryStub.GetCountries().AsQueryable().BuildMockDbSet(); // 3 items (1st is Id:1)
            _uow.Setup(x => x.Context.Set<Country>()).Returns(countries.Object);
            _uow.Setup(x => x.Context.Set<Country>().FindAsync(1)).ReturnsAsync(new Country { Id:1 }); // and I dont want to do this, collection contains that item already
            var repository = new CountryRepository(_uow.Object);
            var result = await repository.FindByIdAsync(1); //it calls FindAsync internally and result is null

and I would expect once I mock context Country that FindAsync method should return item with id:1

romantitov commented 4 years ago

@risogolo thanks for the issue. I'm not sure that _uow.Setup(x => x.Context.Set<Country>().FindAsync(1)).ReturnsAsync(new Country { Id:1 }) is proper way how FindAsync should be configured. I would suggest you more carefully have a look similar issue https://github.com/romantitov/MockQueryable/issues/7 with code example. in short, try to set up FindAsync for countries instead of _uow. Please let me know if it helped.

risogolo commented 4 years ago

Im sorry it still does not work on my side

private Mock<IOrderManagerUnitOfWork<OrderManagerContext>> _uow = new Mock<IOrderManagerUnitOfWork<OrderManagerContext>>();
 var countries = CountryStub.GetCountries().AsQueryable().BuildMockDbSet(); // GetCountries()r returns List<Country>
            countries.Setup(x=>x.FindAsync(1)).ReturnsAsync((object[] ids) =>
            {
                return countries.Object.FirstOrDefault(x => x.Id == 1);
            }); //also why I should do this, Im basically mocking the data (DbSet), so I expect the linq extension methods will work on mocked collection
            _uow.Setup(x => x.Context.Set<Country>()).Returns(countries.Object);
            var repository = new CountryRepository(_uow.Object); //passing context containing mocked DbSet wrapped in UOW
            var result = await repository.FindByIdAsync(1); //internally calls FindAsync(id)
            Assert.IsNotNull(result); //still null
romantitov commented 4 years ago

@risogolo instead of return countries.Object.FirstOrDefault(x => x.Id == 1); I would suggest CountryStub.GetCountries().FirstOrDefault(x => x.Id == 1); because FindAsync should return items from real data, you can also see it in my example #7

Not sure if the line will work: _uow.Setup(x => x.Context.Set<Country>()).Returns(countries.Object); not sure if the part of code x.Context.Set<Country>() will return you your mock object. I would suggest you to implement IOrderManagerUnitOfWork in a test class and then use it in your tests. Something like this:

internal class MyTestClass: IOrderManagerUnitOfWork<OrderManagerContext>{
    DbSet<Country> _countryDbset;
   public MyTestClass(DbSet<Country> countryDbset){
           _countryDbset = countryDbset;
    }
   ...
    public Set<Country>
   {
         get{
               return _countryDbset;
          }
   }
   ...
} 
risogolo commented 4 years ago

My intention was to mock data in DbSet, not FindAsync method, I expected that FindAsync will return the data from mocked DbSet.

Anyway, so the first suggestion to return CountryStub.GetCountries().FirstOrDefault(x => x.Id == 1);instead does not work and this _uow.Setup(x => x.Context.Set<Country>()).Returns(countries.Object); for getting the list from repository works correctly for the tests where I'm testing collections to be returned from repository method, but not for the FindAsync

risogolo commented 4 years ago

but this works

 countries.Setup(x => x.FindAsync(It.IsAny<object[]>())).ReturnsAsync((object[] ids) =>
            {
                return CountryStub.GetCountries().FirstOrDefault(x => x.Id == 1);
            }); 

but this is not

object[] args = new object[1];
args[0] = 1;
 countries.Setup(x => x.FindAsync(args)).ReturnsAsync((object[] ids) =>
            {
                return CountryStub.GetCountries().FirstOrDefault(x => x.Id == 1);
            });
romantitov commented 4 years ago

If you can provide full code, I'll help you more. but looks like the issue is not related to the project.

romantitov commented 4 years ago

@risogolo FindAsync(It.IsAny<object[]>() works as expected for me.

  var mock = users.AsQueryable().BuildMockDbSet();
      mock.Setup(x => x.FindAsync(It.IsAny<object[]>())).ReturnsAsync((object[] ids) =>
      {
        var id = (Guid) ids.First();
        return users.FirstOrDefault(x => x.Id == id);
      });

I have added a unit test for FindAsync case as example: DbSetFindAsyncUserEntity