【发布时间】:2016-06-24 14:03:57
【问题描述】:
我正在尝试测试以下代码:
public async Task<Activity> Get(long ID, Recruiter User, bool IsArchived = false)
{
Activity result = await collection.FirstOrDefault(x => x.ID == ID && x.Recruiter.CompanyID == User.CompanyID && (!x.Archived || IsArchived));
return result;
}
通过以下测试:
[TestMethod]
public async Task GetDoesThings()
{
long ID = 1;
bool IsArchived = false;
Recruiter User = new Recruiter()
{
CompanyID = 1
};
ActivitiesMock.Setup(x => x.FirstOrDefault(y => y.ID == ID && y.Recruiter.CompanyID == User.CompanyID && (!y.Archived || IsArchived))).ReturnsAsync(new Activity());
Activity result = await repo.Get(ID, User);
ActivitiesMock.Verify(x => x.FirstOrDefault(y => y.ID == ID && y.Recruiter.CompanyID == User.CompanyID && (!y.Archived || IsArchived)));
}
(我知道有不同的编写方式,这是我们尝试过的最新迭代。)
ActivitiesMock 与 Get(long ID, Recruiter User, bool IsArchived = false) 中的 collection 相关。我们最近编写了包装器以尝试更有效地测试我们的实体调用,但在尝试验证调用是否正确时遇到了这个错误:
测试方法 ExampleProject.Tests.Backend.Repositories.ActivityRepositoryTests.GetDoesThings 抛出异常: Moq.MockException: 对模拟的预期调用至少一次,但从未执行:x => x.FirstOrDefault(y => (y.ID == .ID && y.Recruiter.CompanyID == .User.CompanyID) && (!(y .Archived) || .IsArchived))
配置的设置: x => x.FirstOrDefault(y => (y.ID == .ID && y.Recruiter.CompanyID == .User.CompanyID) && (!(y.Archived) || .IsArchived)), Times.Never
执行的调用: IAppCollection`2.FirstOrDefault(x => (((x.ID == value(ExampleProject.Backend.Repositories.ActivityRepository+c__DisplayClass2_0).ID) AndAlso (x.Recruiter.CompanyID == value(ExampleProject.Backend.Repositories. ActivityRepository+c__DisplayClass2_0).User.CompanyID)) AndAlso (Not(x.Archived) OrElse 值(ExampleProject.Backend.Repositories.ActivityRepository+c__DisplayClass2_0).IsArchived)))
在这种情况下,包装器 (collection) 是接口的模拟。目标是确保存储库调用包装器上的正确表达式,以便我们知道传递给实体 DbSet 的谓词是正确的,而不必担心所有混乱的异步抽象。
运行测试时似乎找不到带有完整谓词的 Mock Setup(),当我将 Setup() 更改为 It.IsAny<Expression<Func<Activity, bool>>>() 时,它运行模拟并提供返回,但 @987654330 @调用不起作用。所以,运行:
ActivitiesMock.Setup(x => x.FirstOrDefault(It.IsAny<Expression<Func<Activity, bool>>>())).ReturnsAsync(new Activity());
Activity result = await repo.Get(ID, User);
Assert.IsNotNull(result);
ActivitiesMock.Verify(x => x.FirstOrDefault(y => y.ID == ID && y.Recruiter.CompanyID == User.CompanyID && (!y.Archived || IsArchived)));
在运行时通过了断言但验证失败:
ActivitiesMock
.Setup(x => x.FirstOrDefault(y => y.ID == ID && y.Recruiter.CompanyID == User.CompanyID && (!y.Archived || IsArchived)))
.ReturnsAsync(new Activity())
.Verifiable();
Activity result = await repo.Get(ID, User);
Assert.IsNotNull(result);
ActivitiesMock.Verify();
断言失败。
看起来它失败了,因为它需要相同的对象类型。我是在尝试做一些起订量无法处理的事情,还是我错过了为了让验证正确而需要做的事情?
根据要求,LINQ-to-Entity wrapper (collection) 的具体实现是:
public Task<T> FirstOrDefault(Expression<Func<T, bool>> Predicate)
{
return DbSet.FirstOrDefaultAsync(Predicate);
}
虽然包装器本身没有被使用,但它的接口正在被模拟,而我们正在测试的就是那个模拟。
【问题讨论】:
-
对
FirstOrDefault的调用是常用的LINQ 方法吗?如果是这样,您不能使用 Moq 模拟它,因为它是一种扩展方法。 -
不是。它是 LINQ-to-Entities 方法的包装器。
-
您验证是否调用了默认 LINQ
FirstOrDefault方法,但事实并非如此。你能把你的包装贴出来吗? -
我不确定我是否清楚。我没有使用包装器的实际实现,而是对其进行模拟。我正在尝试验证存储库对包装器的调用是否正确。我可以发布一个实现,但我不确定它是否会有所帮助,因为我使用的是界面模拟。
-
我更新了问题以包含实现。正如我所说,它没有被调用,因为包装器本身正在被模拟,我们要做的就是验证是否使用了正确的表达式。