【问题标题】:Should simple classes be mocked in unit-tests or should they be used/tested directly?应该在单元测试中模拟简单的类还是应该直接使用/测试它们?
【发布时间】:2020-03-19 09:35:53
【问题描述】:

我不确定我的方法 TranslateResponse() 还可以测试什么。

它基本上检查翻译器的类型并调用翻译器的关联set()方法。

public async Task TranslateResponse(Policy response)
{
    foreach (var t in await _translatorFactory.BuildTranslators())
    {
        var policyTranslator = t as IPolicyAwareTranslator;
        policyTranslator?.SetPolicy(response);
        var additionalInterestTranslator = t as IAdditionalInterestAwareTranslator;    
        additionalInterestTranslator?.SetAdditionalInterests(response.AdditionalInterests);
        var locationsTranslator = t as ILocationsAwareTranslator;
        locationsTranslator?.SetLocations(response.Locations);
    }
}

我正在为TranslateResponse() 方法编写测试用例。据我所知,我正在验证对相应方法的调用是否基于提供的翻译器类型发生。 测试用例行

Mock<ITranslator> mockedTranslator = new Mock<ITranslator>(); 
mockedTranslator.Setup(t => t.Translate(_translatorDataAccessor.Object));

var mockedPolicyTranslator = mockedTranslator.As<IPolicyAwareTranslator>();
mockedPolicyTranslator.Setup(t => t.SetPolicy(It.IsAny<Policy>()));

mockedPolicyTranslator.Verify(t => t.SetPolicy(It.IsAny<Policy>()), Times.AtLeastOnce);

我的担忧是

  1. 我很想知道我是否可以测试比验证调用更多的东西?

  2. 我应该在这里测试 set() 方法的逻辑还是在它自己的类中测试?甚至,我无法弄清楚在Set() 的测试用例中要断言什么,它将使用传入的参数设置私有字段。

public class PolicyTranslator : ITranslator, IPolicyAwareTranslator
{      
    private Policy _policy;        
    public void SetPolicy(Policy policy)
    {
        _policy = policy;
    }  
    //translate()
}      

【问题讨论】:

    标签: c# unit-testing mocking moq xunit.net


    【解决方案1】:

    除了验证调用之外,我是否可以测试其他内容?

    基本上没有什么可以测试的了,只是

    1. 为每个使用的接口添加相同的测试

    另外,你可能想要

    1. 测试实现所有接口的东西实际上是作为每个接口处理的
    2. 也许还有一个测试来验证是否存在 foreach 循环(多个翻译器),而不仅仅是 .FirstOrDefault()
    3. 此外,没有元素返回 BuildTranslators 的情况也是有效的。

    我应该在这里测试 set() 方法的逻辑还是在它自己的类中测试?

    在大多数情况下,不应在单元测试中测试 SUT 依赖项,因为

    1. stop 可能是一个单元测试(如果依赖项足够复杂的话)- 并不是那么重要(除非您对单元测试保持非常严格的定义),但正如我们在下面看到的那样。
    2. 通过其消费者测试依赖项通常会使测试变得不必要地复杂化
      • 您必须自己设置(通常是重要的)依赖项。
      • 您必须自行设置其使用者/用户/SUT。
      • 然后您必须以实际调用的方式构建测试 依赖。
      • 然后验证依赖项及其使用者是否按预期工作。
      • 并且测试的复杂性/数量不会线性增长 - 如果您可以通过 4 个 SUT 测试和 4 个依赖项测试获得 100%/合理的覆盖率,而通过集成级别测试实现相同程度的覆盖率可能需要介于两者之间4 和 4*4 甚至更多的测试。

    对于简单的情况,例如,有问题的代码实际上会简化事情,因为您不再需要模拟 IPolicyAwareTranslator 并且可以简单地测试值是否按预期设置,但即使在这种情况下是一个小陷阱:

    1. 我们无法再确定该方法是否处理 IPolicyAwareTranslator - 有人可以将 var policyTranslator = t as IPolicyAwareTranslator; 替换为 var policyTranslator = t as PolicyTranslator; 并且测试仍将通过,尽管它不再能够处理我们的业务关键型 AwesomePolicyAwareTranslator。李>

    你可能会说它基本上是movie-plot-threat,如果你知道自己在做什么,那么简单地将多个类作为一个子系统进行测试,并且不会太费力。

    虽然在大多数情况下我会反对这种“低级集成测试”,因为你可以有适当的隔离,更简单!单元测试

    低级集成测试 - 这基本上是一个虚构的术语,但我想说它非常适合此类测试。


    不过,在某些情况下,粗粒度的“低级集成测试”可能比覆盖相同代码的单元测试略好:

    1. 通常会发生这种情况,当单个组件最终变得如此之小,以至于对它们进行单元测试时,您无法表达领域概念/在众多真实场景​​中没有任何真正程度的信任,而在更高的测试 test pyramid太少/难以运行/维护。

    在这种情况下,“低级集成测试”以合理的价格为您提供了很多东西(我们不应该忘记 100% 的覆盖率是无法获得的,正确的测试金字塔不能以便宜的方式构建,因此具有成本效益的方法测试是必要的)。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2018-02-21
      • 1970-01-01
      • 2011-06-22
      • 1970-01-01
      • 2011-01-29
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多