【问题标题】:Verify that linq extension methods are called with moq验证是否使用 moq 调用了 linq 扩展方法
【发布时间】:2024-05-04 21:15:02
【问题描述】:

我有一个这样的方法:

    public IEnumerable<string> GetCompaniesCities()
    {
        return CompanyRepository.All()
                      .OrderBy(c => c.City)
                      .Select(c => c.City.ToUpper())
                      .Distinct().ToList();
    }

如何测试(使用最小起订量)OrderBy (c =&gt; c.City) 被调用?还有Select(c =&gt; c.City.ToUpper()Distinct()

我来了

对非虚拟(在 VB 中可覆盖)成员的验证无效:x => x.All().OrderBy(c => c.City)

注意:这是单元测试,不是集成测试,所以我不会去数据库

【问题讨论】:

  • 不要像这样测试内部,而是测试方法是否返回它应该返回的内容以及它是否按正确的顺序。
  • 问题是它是单元测试,不是集成测试,所以我不会去db
  • 这不是避免集成测试的方法。您应该改为模拟存储库并在其中注入您的内存数据(查看依赖注入和框架,如 unity 和 ninject)。
  • 根据您的数据库设置,您可能希望在进行 ToUpper 转换后进行 OrderBy。如果您的数据库区分大小写,则如果您混合了大小写城市,则城市将无法正确排序。

标签: c# linq unit-testing moq


【解决方案1】:

您不会测试 OrderBy 是否被调用,只测试返回的字符串是否有序会容易得多。即,使用 NUnit:

Assert.That(sut.GetCompaniesCities(), Is.Ordered);

还有 Moq 你can't test that an extension method like OrderBy is called,这是不可能的。


为了解决您关于无法访问数据库的评论,您是对的,模拟您的依赖项消除了访问数据库的需要。但是您可以模拟它们的返回值,以便模拟它们确实 访问数据库。因此,在这里,您的存储库是一个依赖项,您将模拟 All() 以返回未排序值的列表:

var companiesUnsorted = new Company[] { 
    new Company { City = "Xyz" },
    new Company { City = "Abc" }
};
var mockRepository = new Mock<ICompanyRepository>();
mockRepository.Setup(p => p.All()).Returns(companiesUnsorted);

var sut = new WhateverClassYourCodeSampleIsFrom(mockRepository.Object);

那么你只需执行上面的断言。

【讨论】:

  • 问题是它是单元测试,不是集成测试,所以我不会去db
  • 没关系,大概您正在设置您的存储库以返回一个列表,对吧?只需让.All() 返回一个未排序的列表,并验证GetCompaniesCities() 的输出是否已排序。无需数据库交互...
【解决方案2】:

您不能仅仅因为moq 不能mock 扩展方法而这样做。这是因为moq 使用继承,而扩展方法没有

【讨论】:

    【解决方案3】:

    您要做的是在您的单元测试中将 CompanyRepository 对象的 All 方法最小化。将 CompanyRepository Moq 注入到包含 GetCompaniesCities() 方法的类中。

    然后适当地填充最小起订量并让您的单元测试验证 GetCompaniesCities() 方法返回预期结果。您不关心 GetCompaniesCities 是如何编码的,只是它返回正确的值。

    【讨论】: