如何通过 Moq 为单元测试中的模拟设置具体的 Expression<Func<TEntity, bool>>?
How to set up a concrete Expression<Func<TEntity, bool>> to a mock in a unit test through Moq?
我的存储库中有以下方法:
Task<TEntity> FirstOrDefaultAsync(
Expression<Func<TEntity, bool>> filter = null,
Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null,
CancellationToken cancellationToken = default);
我在服务中是这样调用的:
var itemToUpdate = await _unitOfWork.ItemsRepository
.FirstOrDefaultAsync(x => x.Id == itemDto.Id && x.UserId == userId);
问题是如何在单元测试中调用它?我对 ItemsRepository 有一个严格的模拟,当我这样调用它时,我得到一个异常,它是一个严格的模拟并且没有为我的场景设置:
ItemsRepositoryMock
.Setup(x => x.FirstOrDefaultAsync(x => x.Id == 1 && x.UserId == "ab70793b-cec8-4eba-99f3-cbad0b1649d0", null, CancellationToken.None))
.ReturnsAsync(firstItem);
我应该如何使用我想要的参数设置模拟?
简短的回答是您不能直接在 Moq 中设置它。原因是就 c# 而言,这些表达式不相等。
你能做什么...
是编写您自己的表达式比较,根据该表达式中允许的内容,它可能更容易或更难。你用
设置模拟
It.Is<Expression<Func<TheEntity, bool>>(actual=>CompareExpressions(actual, x=>x => x.Id == 1 && x.UserId == "ab70793b-cec8-4eba-99f3-cbad0b1649d0")
并实施
CompareExpressions<TEntity>(Func<TEntity, bool> actual,Func<TEntity, bool> expected)
遍历表达式树并在检测到差异时执行 Assert.Fail 这并不难,但如果您之前没有这样做,这可能是一个挑战,特别是如果您希望允许等效和不相同的表达式.开始的好点是:
ExpressionComparison
或者您可以编译表达式并使用匹配的元素调用它,您可以这样做
It.Is<Expression<Func<TheEntity, bool>>(actual=>CompareExpressions(actual, new[]{
new Sample<TheEntity>{ Subject= new TheEntity{ /*...*/ }, Expected = true,
new Sample /*...*/ )
CompareExpressions(Func<TEntity, bool> actual, List<TEntity> samples)
{
var method = actual.Compile();
foreach(var sample in sampes)
Assert.AreEqual(sample.Expected, method(sample.Subject));
}
第二种方法更简单,但如果您准备的样本不够好,可能会出现误报。
我的存储库中有以下方法:
Task<TEntity> FirstOrDefaultAsync(
Expression<Func<TEntity, bool>> filter = null,
Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null,
CancellationToken cancellationToken = default);
我在服务中是这样调用的:
var itemToUpdate = await _unitOfWork.ItemsRepository
.FirstOrDefaultAsync(x => x.Id == itemDto.Id && x.UserId == userId);
问题是如何在单元测试中调用它?我对 ItemsRepository 有一个严格的模拟,当我这样调用它时,我得到一个异常,它是一个严格的模拟并且没有为我的场景设置:
ItemsRepositoryMock
.Setup(x => x.FirstOrDefaultAsync(x => x.Id == 1 && x.UserId == "ab70793b-cec8-4eba-99f3-cbad0b1649d0", null, CancellationToken.None))
.ReturnsAsync(firstItem);
我应该如何使用我想要的参数设置模拟?
简短的回答是您不能直接在 Moq 中设置它。原因是就 c# 而言,这些表达式不相等。
你能做什么...
是编写您自己的表达式比较,根据该表达式中允许的内容,它可能更容易或更难。你用
设置模拟It.Is<Expression<Func<TheEntity, bool>>(actual=>CompareExpressions(actual, x=>x => x.Id == 1 && x.UserId == "ab70793b-cec8-4eba-99f3-cbad0b1649d0")
并实施
CompareExpressions<TEntity>(Func<TEntity, bool> actual,Func<TEntity, bool> expected)
遍历表达式树并在检测到差异时执行 Assert.Fail 这并不难,但如果您之前没有这样做,这可能是一个挑战,特别是如果您希望允许等效和不相同的表达式.开始的好点是: ExpressionComparison
或者您可以编译表达式并使用匹配的元素调用它,您可以这样做
It.Is<Expression<Func<TheEntity, bool>>(actual=>CompareExpressions(actual, new[]{ new Sample<TheEntity>{ Subject= new TheEntity{ /*...*/ }, Expected = true, new Sample /*...*/ ) CompareExpressions(Func<TEntity, bool> actual, List<TEntity> samples) { var method = actual.Compile(); foreach(var sample in sampes) Assert.AreEqual(sample.Expected, method(sample.Subject)); }
第二种方法更简单,但如果您准备的样本不够好,可能会出现误报。