我正在尝试测试以下代码:
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)
中找到.我们最近编写了包装器,以便更有效地尝试和测试我们的实体调用,但是在尝试验证调用是否正确时,我们遇到了这个错误:
Test method ExampleProject.Tests.Backend.Repositories.ActivityRepositoryTests.GetDoesThings threw exception: Moq.MockException: Expected invocation on the mock at least once, but was never performed: x => x.FirstOrDefault(y => (y.ID == .ID && y.Recruiter.CompanyID == .User.CompanyID) && (!(y.Archived) || .IsArchived))
Configured setups: x => x.FirstOrDefault(y => (y.ID == .ID && y.Recruiter.CompanyID == .User.CompanyID) && (!(y.Archived) || .IsArchived)), Times.Never
Performed invocations: 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 value(ExampleProject.Backend.Repositories.ActivityRepository+<>c__DisplayClass2_0).IsArchived)))
在此实例中,包装器 ( collection
) 是接口(interface)的模拟。目标是确保存储库在包装器上调用正确的表达式,以便我们知道传递给实体 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);
}
虽然包装器本身没有被使用,但是它的一个接口(interface)正在被模拟,我们正在测试的就是那个模拟。
最佳答案
答案都不是。由于匿名函数必须创建类实例来存储所提供的数据,因此它们会创建 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));
现在承认它有点 hacky,但乍一看它似乎不太像垃圾箱火灾解决方案,这是我们确保提供的调用按照我们期望的方式进行的一种方式,这就是我们想要。
更新(2016 年 9 月 7 日):到目前为止,这对我们来说效果很好。我们遇到了 LINQ-to-Entity 语句未按预期运行的问题,因为 LINQ 区分大小写而生成的 SQL 不区分大小写,但由于这对我们来说不是一个交易破坏者,我们只是很好。
关于c# - 这是错误的起订量设置还是起订量不足?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38015326/