我正在尝试测试以下代码:
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
与collection
中找到的Get(long ID, Recruiter User, bool IsArchived = false)
相关。我们最近编写了包装器,以便更有效地测试我们的实体调用,但是在尝试验证调用是否正确时,我们遇到了这个错误:
测试方法ExampleProject.Tests.Backend.Repositories.ActivityRepositoryTests.GetDoesThings抛出异常: Moq.MockException: 模拟上的预期调用至少一次,但从未执行过:x =&gt; x.FirstOrDefault(y =&gt;(y.ID == .ID&amp;&amp; y.Recruiter.CompanyID == .User.CompanyID)&amp;&amp;(!(y.Archived)|| .IsArchived))< / p>
已配置的设置: x =&gt; x.FirstOrDefault(y =&gt;(y.ID == .ID&amp;&amp; y.Recruiter.CompanyID == .User.CompanyID)&amp;&amp;(!(y.Archived)|| .IsArchived)), Times.Never
执行调用: IAppCollection`2.FirstOrDefault(x =&gt;(((x.ID == value(ExampleProject.Backend.Repositories.ActivityRepository +&lt;&gt; c__DisplayClass2_0).ID)AndAlso(x.Recruiter.CompanyID == value(ExampleProject。 Backend.Repositories.ActivityRepository +&lt;&gt; c__DisplayClass2_0).User.CompanyID))AndAlso(Not(x.Archived)OrElse值(ExampleProject.Backend.Repositories.ActivityRepository +&lt;&gt; c__DisplayClass2_0).IsArchived)))
在这个例子中,包装器(collection
)是接口的模拟。目标是确保存储库在包装器上调用正确的表达式,以便我们知道传递给Entity DbSet的谓词是正确的,而不必担心所有混乱的异步抽象。
运行测试时似乎找不到包含完整谓词的模拟Setup()
,当我将Setup()
更改为It.IsAny<Expression<Func<Activity, bool>>>()
时,它会运行模拟并提供返回,但Verify
调用不起作用。所以,运行:
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();
断言失败。
看起来它失败了,因为它期待相同的对象类型。我试图做一些Moq无法处理的事情,或者我错过了一些我需要做的事情才能使验证正确无误?
根据请求,LINQ-to-Entity包装器(collection
)的具体实现是:
public Task<T> FirstOrDefault(Expression<Func<T, bool>> Predicate)
{
return DbSet.FirstOrDefaultAsync(Predicate);
}
虽然没有使用包装器本身,但是它的界面被嘲笑,而且我们正在测试它的模拟。
答案 0 :(得分:2)
答案都不是。由于匿名函数必须创建类实例来存储所提供的数据,因此它们会创建DisplayClass
个实例来保存数据。由于这些实例是在不同的命名空间(以及其他内容)中创建的,因此当Moq对它们调用.Equals
时,它们不会通过。
我们通过编写测试来解决这个问题:
ActivitiesMock
.Setup(x => x.Where(It.IsAny<Expression<Func<Activity, bool>>>()))
.Returns((Expression<Func<Activity, bool>> x) =>
{
actualPredicate = x;
return queryMock.Object;
});
然后创建有效和无效的活动以提供给谓词,以确保它正确返回true
或false
:
Assert.IsTrue(actualPredicate.Compile().Invoke(validActivity));
现在说它有点黑客,但乍一看它看起来不像垃圾箱解决方案,而且它是我们确保所提供的电话做什么的一种方式我们期待他们,这就是我们想要的。
更新(2016年9月7日):到目前为止,这对我们来说一直很好。我们遇到过LINQ-to-Entity语句没有按预期运行的问题,因为LINQ区分大小写并且生成的SQL不是,但是因为那不是交易对我们而言,我们很好。