Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Mocking Entity Framework Core context

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

like image 544
bielu000 Avatar asked Nov 29 '17 13:11

bielu000


4 Answers

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 :)

like image 164
cdev Avatar answered Oct 16 '22 10:10

cdev


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.

like image 37
Chris Pratt Avatar answered Oct 16 '22 10:10

Chris Pratt


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
like image 20
R.Titov Avatar answered Oct 16 '22 10:10

R.Titov


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.

like image 1
Serj Sagan Avatar answered Oct 16 '22 10:10

Serj Sagan