我编写了一个通用存储库(BaseRepository),其中删除方法代码是:
public virtual void Delete(TEntity entity)
{
if (dbContext.Entry(entity).State == EntityState.Detached)
{
dbContext.Attach(entity);
}
dBContext.Remove(entity);
}
我想对代码进行单元测试。由于 DbContext 是外部依赖项,我只想验证当我调用 Repository.Delete(entity) 时,最终 DbContext.Remove(entity) 是被调用一次。但是,我必须在实际 dbContext.Remove(entity) 调用之前模拟 dbContext.Entry 的行为。 单元测试代码为:
[Fact]
public void Delete_SomeEntityToRepository_CallsTheAddMethod_To_DbContext()
{
// Arrange
var testObject = new Customer()
{
Name = "Test-Customer"
};
var dbContextMock = new Mock<DbContext>();
var dbEntityEntryMock = new Mock<EntityEntry<Customer>>();
dbContextMock.Setup(d => d.Entry(testObject)).Returns(dbEntityEntryMock.Object);
dbEntityEntryMock.Setup(e => e.State).Returns(EntityState.Unchanged);
// Act
var repository = new CustomerRepository(dbContextMock.Object);
repository.Delete(testObject);
//Assert
dbContextMock.Verify(x => x.Remove(It.Is<Customer>(y => y == testObject)), Times.AtMost(1));
}
但是这段代码崩溃了,实际上是在以下行:
dbContextMock.Setup(d => d.Entry(testObject)).Returns(dbEntityEntryMock.Object);
带有消息:
无法实例化类的代理:Microsoft.EntityFrameworkCore.ChangeTracking.EntityEntry`1[[Fx.CommonTests.DataAccess....
系统参数异常 无法实例化类的代理:Microsoft.EntityFrameworkCore.ChangeTracking.EntityEntry`1[[Fx.CommonTests.DataAccess.Customer, Fx.CommonTests, Version=4.3.0.0, Culture=neutral, PublicKeyToken=null ]]。 找不到无参数构造函数。 (参数“构造函数参数”)...
显然 DbContext.Entry(...) 是问题所在。那么,关于如何 mock 这个的任何想法???
我尝试了代码的各种变体,如几篇文章中所示,关于如何模拟此类情况,但结果总是相同的。有什么想法吗?
最佳答案
这就是您可以模拟 dbcontext 来防止测试错误的方法,但我相信在设计此测试时可能需要考虑不同的方法(请参阅下面的 but
部分):
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.ChangeTracking;
using Microsoft.EntityFrameworkCore.ChangeTracking.Internal;
using Microsoft.EntityFrameworkCore.Metadata;
using Moq;
// ...
[Fact]
public void Delete_SomeEntityToRepository_CallsTheAddMethod_To_DbContext()
{
// Arrange
var testObject = new Customer();
var internalEntityEntry = GetInternalEntityEntry(testObject);
var dbEntityEntryMock = new Mock<EntityEntry<Customer>>(internalEntityEntry);
dbEntityEntryMock.Setup(e => e.State).Returns(EntityState.Unchanged);
var dbContextMock = new Mock<DbContext>();
dbContextMock.Setup(d => d.Entry(testObject)).Returns(dbEntityEntryMock.Object);
// Act
var repository = new CustomerRepository(dbContextMock.Object);
repository.Delete(testObject);
//Assert
dbContextMock.Verify(x => x.Remove(It.Is<Customer>(y => y == testObject)), Times.AtMost(1));
}
private static InternalEntityEntry GetInternalEntityEntry(Customer testObject)
{
return new InternalEntityEntry(
new Mock<IStateManager>().Object,
new RuntimeEntityType(
name: nameof(Customer), type: typeof(Customer), sharedClrType: false, model: new(),
baseType: null, discriminatorProperty: null, changeTrackingStrategy: ChangeTrackingStrategy.Snapshot,
indexerPropertyInfo: null, propertyBag: false,
discriminatorValue: null),
testObject);
}
全文如下:https://gist.github.com/ctrl-alt-d/3d10384a06fa1e0c515e1f182fb83bb0
但是(<- 大“但是”):
- 你不应该这样做: https://github.com/dotnet/efcore/issues/27110
- 了解如何在使用 EF 时编写测试 https://learn.microsoft.com/en-us/ef/ef6/fundamentals/testing/mocking
- 如果我要为您的通用存储库编写一个测试,我将采用以下方法:https://gist.github.com/ctrl-alt-d/8bc8d1f9e41a8ea98309397c46933fe4
关于c# - 处理 DbContext.Entry 时尝试基于 .NET EF Core 对通用存储库进行单元测试失败,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/76450849/