简体   繁体   English

模拟实体框架核心上下文

[英]Mocking Entity Framework Core context

I try to test my app so I need to mock my EF context. 我尝试测试我的应用程序,所以我需要模拟我的EF上下文。

My code seems to be ok, but I have following exception: 我的代码似乎还可以,但是我有以下异常:

"System.ArgumentNullException : Value cannot be null. Parameter name: source" “ System.ArgumentNullException:值不能为空。参数名称: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 它来自我正在测试的服务

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. 坦白说,要完全模拟DbContext有点困难,这就是EF Core团队提供内存中实现的原因。 This is far easier to work with for testing purposes. 为了测试目的,这要容易得多。 Just change your options initialization to: 只需将您的options初始化更改为:

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. 内存数据库本质上是一个模拟。

I have used this https://github.com/huysentruitw/entity-framework-core-mock library. 我已经使用了这个https://github.com/huysentruitw/entity-framework-core-mock库。 Very easy and can write unit test using less coding. 非常简单,可以使用较少的编码编写单元测试。

You can use most of Moq methods if you are using moq framework. 如果您使用的是moq框架,则可以使用大多数Moq方法。

Below is example code for test DBQuerys. 以下是测试DBQuery的示例代码。

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 :) 在这里发布可以帮助某人:)

I don't think it's correct to Mock the DbContext . 我认为Mock 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. 您应该在测试中mocking您的repositories ... mocking DbContext是您基本上在测试Microsoft's代码...这是愚蠢的,因为他们已经这样做了。 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 . 如此反复......所有的数据访问应该通过repositories (见Repository模式 ),你应该mocking那些在您的测试,而不是DbContext

Try to use my Moq/NSubstitute extension MockQueryable: https://github.com/romantitov/MockQueryable supported all Sync/Async operations 尝试使用我的Moq / NSubstitute扩展名MockQueryable: https : //github.com/romantitov/MockQueryable支持所有Sync / Async操作

//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 还支持DbSet

//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 从1.0.4版本开始支持AutoMapper
  • DbQuery supported from 1.1.0 ver 从1.1.0版本开始支持DbQuery

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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