c# - 模拟异步查询操作
问题描述
我创建了单元测试,我必须模拟context
我的 EF。我使用Moq
库和Xunit
. 我有一个这样的测试方法:
[Fact]
public async Task DeleteAttachmentCommandHandler_WithValidCommand_ShouldCallSaveChangesAsyncOnce()
{
var command = new DeleteAttachmentCommand { Id = Guid.NewGuid() };
var attachments = new List<Attachment>();
var dbSetMock = attachments.AsQueryable().BuildMockDbSetForAsyncQueryOperations();
_context.Setup(x => x.Set<Attachment>()).Returns(dbSetMock.Object);
_context.Setup(x => x.SaveChangesAsync(It.IsAny<CancellationToken>())).ReturnsAsync(1).Verifiable();
await Act(command);
_context.Verify(x => x.SaveChangesAsync(It.IsAny<CancellationToken>()), Times.Once);
}
是_context
类型Mock<IEmployeeSettlementsDbContext>
这BuildMockDbSetForAsyncQueryOperations
是我的扩展方法,这要归功于 MSDN 文档 -> https://docs.microsoft.com/en-us/ef/ef6/fundamentals/testing/mocking,它使用了异步提供程序,如TestDbAsyncEnumerable
、TestDbAsyncEnumerator
、TestDbAsyncQueryProvider
。我的扩展BuildMockDbSetForAsyncQueryOperations
看起来像这样:
public static Mock<IDbSet<TEntity>> BuildMockDbSetForAsyncQueryOperations<TEntity>(this IQueryable<TEntity> data) where TEntity : class
{
var dbSetMock = new Mock<IDbSet<TEntity>>();
dbSetMock.As<IDbAsyncEnumerable<TEntity>>().Setup(x => x.GetAsyncEnumerator()).Returns(new TestDbAsyncEnumerator<TEntity>(data.GetEnumerator()));
dbSetMock.As<IQueryable<TEntity>>().Setup(x => x.Provider).Returns(new TestDbAsyncQueryProvider<TEntity>(data.Provider));
dbSetMock.As<IQueryable<TEntity>>().Setup(x => x.Expression).Returns(data.Expression);
dbSetMock.As<IQueryable<TEntity>>().Setup(x => x.ElementType).Returns(data.ElementType);
dbSetMock.As<IQueryable<TEntity>>().Setup(x => x.GetEnumerator()).Returns(data.GetEnumerator());
return dbSetMock;
}
在我正在测试的实际处理程序方法中,我有一行:
var attachment = await _context.Set<Attachment>()
.SingleAsync(x => x.Id == command.Id);
当我运行测试它失败并向我显示消息
消息:System.InvalidOperationException:序列不包含匹配元素。
但是,当我将处理程序中的查询更改为,ListAsync()
而不是SingleAsync()
模拟设置正确并返回我一个空列表时。但它不适用于使用SingleAsync()
.
编辑:这是我的完整处理程序方法:
public async Task<Unit> Handle(DeleteAttachmentCommand command, CancellationToken cancellationToke = default(CancellationToken))
{
ValidateCommand(command);
var attachment = await _context.Set<Attachment>().SingleAsync(x => x.Id == command.Id);
attachment.IsDeleted = true;
await _context.SaveChangesAsync();
return Unit.Value;
}
解决方案
推荐阅读
- wpf - 风格错误?无法在 Windows10 中的样式中设置背景颜色
- android - 如何在 Android Studio 中修复 'Context.getResources()' 上的空对象引用'
- ios - NSInMemoryStoreType 类型的核心数据忽略实体的约束
- django - Conditional in model
- scala - 检查定义了多少对象属性
- python-2.7 - re.split() 在一个复杂的字符串上
- selenium - send_keys (Keys.ENTER) 上的 selenium webdriver 错误
- r - 如何计算同一区域不同分类的层堆栈中最丰富的像素值(多数票)?
- mysql - 如何将具有相同PK的项目中的数据添加在一起
- visual-studio-code - 如何返回在 VScode 中打开的上一个选项卡