Mocking Entity Framework Core context

有些话、适合烂在心里 提交于 2019-12-09 10:45:25

问题


I try to test my app so I need to mock my EF context.

My code seems to be ok, but I have following exception:

"System.ArgumentNullException : Value cannot be null. Parameter name: source"

Here is my test method:

  var options = new DbContextOptionsBuilder<ProductContext>().Options;
    var settings = new SqlSettings
    {
        InMemory = true
    };

    var context = new Mock<ProductContext>(options, settings);
    var mockTreeService = new TreeService(context.Object);
    await mockTreeService.CreateTreeAsync("Testing tree", Guid.NewGuid());

    context.Verify(x => x.AddAsync(It.IsAny<Tree>(), CancellationToken.None), Times.Once);

It looks like that this exception is thrown during executing this piece of code

            var tree = await _context.Trees
                .Include(x => x.Translation)
                .FirstOrDefaultAsync(x => x.Translation.Pl == name);

It comes from my service which I'm testing


回答1:


I think this is due to not having a connection string set. Frankly, it's a bit difficult to fully mock out DbContext, which is why the EF Core team has provided an in-memory implementation. This is far easier to work with for testing purposes. Just change your options initialization to:

var options = new DbContextOptionsBuilder<ProductContext>()
                  .UseInMemoryDatabase(Guid.NewGuid().ToString())
                  .Options;

Afterwards, you'll need to populate the database with your test data. Then, you can run the rest of your test.

Note: if you're using the in-memory database, you don't need to mock the context anymore, so you can remove that bit of code. The in-memory database is essentially, itself, a mock.




回答2:


I have used this https://github.com/huysentruitw/entity-framework-core-mock library. Very easy and can write unit test using less coding.

You can use most of Moq methods if you are using moq framework.

Below is example code for test DBQuerys.

public async Task<Boat> GetByIdAsync(string id)
    => await _boatContext.Boats.Where(x => x.id == id).FirstOrDefaultAsync();

[Fact]
public async Task GetByIdAsync_WhenCalled_ReturnsItem()
{
    // Arrange
    var models = new[] { new Boat { id = "p1" } };
    var dbContextMock = new DbContextMock<BoatContext>();
    dbContextMock.CreateDbQueryMock(x => x.Boats, models);

    var service = new Properties(dbContextMock.Object);

    // Act
    var okResult = await service.GetByIdAsync("p1");

    // Assert
    Assert.IsType<Boat>(okResult.Result);
}

Posting here this may help someone :)




回答3:


I don't think it's correct to Mock the DbContext. You should be mocking your repositories in your testing... mocking the DbContext is you basically testing Microsoft's code... which is dumb because they already do that. So again... all of your data access should go through repositories (see Repository Pattern) and you should be mocking those in your testing, not the DbContext.




回答4:


Try to use my Moq/NSubstitute extension MockQueryable: https://github.com/romantitov/MockQueryable supported all Sync/Async operations

//1 - create a List<T> with test items
var users = new List<UserEntity>()
{
 new UserEntity,
 ...
};

//2 - build mock by extension
var mock = users.AsQueryable().BuildMock();

//3 - setup the mock as Queryable for Moq
_userRepository.Setup(x => x.GetQueryable()).Returns(mock.Object);

//3 - setup the mock as Queryable for NSubstitute
_userRepository.GetQueryable().Returns(mock);

DbSet also supported

//2 - build mock by extension
var mock = users.AsQueryable().BuildMockDbSet();

//3 - setup DbSet for Moq
var userRepository = new TestDbSetRepository(mock.Object);

//3 - setup DbSet for NSubstitute
var userRepository = new TestDbSetRepository(mock);

Note:

  • AutoMapper supported from 1.0.4 ver
  • DbQuery supported from 1.1.0 ver


来源:https://stackoverflow.com/questions/47553878/mocking-entity-framework-core-context

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!