【问题标题】:Is this a bad Moq setup or a deficiency in Moq?这是起订量设置不佳还是起订量不足?
【发布时间】: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)));
}

(我知道有不同的编写方式,这是我们尝试过的最新迭代。)

ActivitiesMockGet(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&lt;Expression&lt;Func&lt;Activity, bool&gt;&gt;&gt;() 时,它运行模拟并提供返回,但 @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 方法,但事实并非如此。你能把你的包装贴出来吗?
  • 我不确定我是否清楚。我没有使用包装器的实际实现,而是对其进行模拟。我正在尝试验证存储库对包装器的调用是否正确。我可以发布一个实现,但我不确定它是否会有所帮助,因为我使用的是界面模拟。
  • 我更新了问题以包含实现。正如我所说,它没有被调用,因为包装器本身正在被模拟,我们要做的就是验证是否使用了正确的表达式。

标签: c# moq


【解决方案1】:

答案都不是。由于匿名函数必须创建类实例来存储提供的数据,它们创建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;
    });

然后创建有效和无效活动以提供给谓词以确保它正确返回truefalse

Assert.IsTrue(actualPredicate.Compile().Invoke(validActivity));

现在承认它有点老套,但乍一看,它似乎不太像垃圾箱火灾解决方案,它是我们确保提供的调用符合我们期望的一种方式,这就是我们想要。

更新(2016 年 9 月 7 日):到目前为止,这对我们来说效果很好。我们遇到了一个问题,即 LINQ-to-Entity 语句未按预期运行,因为 LINQ 区分大小写而生成的 SQL 不区分大小写,但由于这对我们来说不是一个交易破坏者,我们只是很好。

【讨论】:

  • 如果有人想出一个答案,让我们可以轻松地测试 lambda 表达式而无需显着更改测试,我会很高兴提出一个新问题/将其标记为正确。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-08-26
  • 2010-11-30
  • 2019-08-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多