模拟数据库错误的 Mock DbContext

Mock DbContext that simulates a database error

我想对存储库的异常处理代码进行单元测试。我的存储库的一种方法使用 DbContext 的标准 属性,return 是一个 DbSet,然后使用其 AsQueryable 方法,添加 Where 参数,最后调用 ToList 来查询数据库。

我想我需要一个 IQueryable 对象,该对象一直工作到调用其 ToList 扩展方法为止,此时会引发异常。然后我可以将 属性 模拟为 return 一个模拟的 DbSet,并使用模拟的 AsQueryable 方法 return 模拟它。但它不会让我模拟 ToList 方法,因为它是一个扩展方法。

帮忙?

当谈到单元测试和存储库时,存储库应该是模拟的边界,这意味着您模拟存储库以测试依赖于存储库的代码逻辑。 (而不是尝试处理模拟 DbContext)存储库本身应该没有要测试的业务逻辑值,仅用作数据的管道。可以使用不太频繁的 运行 集成测试来测试存储库本身的行为,这些测试方法 运行 具有实际的测试数据源。 (内存数据库或代表测试已知状态的数据库实例)。这样,业务逻辑单元测试可以 运行 在签入之前经常进行,或者在开发过程中定期进行,因为它们将保持快速,而更彻底的端到端集成测试可以 运行不太频繁,例如在构建之前 运行s 的集成期间。

将存储库作为模拟边界,您的测试模拟只需要 return 预期测试代码行为的实体实例、空列表或抛出异常等,具体取决于您所处的行为场景覆盖.

这是允许我测试数据库故障的工作代码示例。感谢@Igor 的灵感!

[Fact]
public void MyTest()
{ 
// Doctor up an IQueryable that will throw an exception when its ToList method is called.
List<MyEntity> list = new List<MyEntity>();
list.Add(new MyEntity());
var expectedEx = new Exception();
var queryable = list.AsQueryable().Where(r => ThrowException(expectedEx));

var mockSet = new Mock<DbSet<MyEntity>>();
mockSet.Setup(m => m.AsQueryable()).Returns(queryable);
mockSet.As<IQueryable<MyEntity>>().Setup(m => m.Provider).Returns(queryable.Provider);
mockSet.As<IQueryable<MyEntity>>().Setup(m => m.Expression).Returns(queryable.Expression);

// Create a mock DbContext that will throw an exception when the repository calls the IQueryable's ToList method.
var mockContext = new Mock<MyContext>(this.options);
mockContext.SetupGet(x => x.Database).Returns(new Mock<DatabaseFacade>(new MyContext(this.options)).Object);
mockContext.Setup(x => x.MyEntityListProperty).Returns(mockSet.Object);

var repository = new MyRepository(mockContext.Object);

// Call the method that calls the IQueryable's ToList, which will throw an exception, simulating a database error.
var thrownEx = Assert.Throws<Exception>(() => repository.FindEntities(Guid.NewGuid()));
Assert.Equal(expectedEx, thrownEx);
}

private static bool ThrowException(Exception e)
{
    throw e;
}