【问题标题】:Is it acceptable to use a 'real' utility class instead of mocking in TDD?使用“真正的”实用程序类而不是在 TDD 中模拟是否可以接受?
【发布时间】:2010-11-05 13:32:24
【问题描述】:

我有一个项目正在尝试学习单元测试和 TDD 实践。我发现我遇到了相当混乱的情况,我花了很长时间为几乎在任何地方都使用的实用程序类设置模拟。

根据我阅读的有关单元测试的内容,如果我正在测试 MyClass,我应该模拟任何其他功能(例如 UtilityClass 提供的功能)。只使用 UtilityClass 而不是为所有不同的测试用例设置 mock 是否可以接受(假设 UtilityClass 本身有一套全面的测试)?

编辑:我正在为此做很多设置的事情之一。 我正在建模一张地图,在不同的位置有不同的对象。我的实用程序类的常用方法之一是 GetDistanceBetween。我正在测试根据其各自属性对事物产生影响的方法,例如,选择一个点 5 个单位内的所有对象且年龄超过 3 岁的测试将需要多次测试(获取范围内的旧对象,忽略旧对象范围,忽略范围内的年轻对象,在每种情况的倍数下都能正常工作)并且所有这些测试都需要设置“GetDistanceBetween”方法。将其乘以使用 GetDistanceBetween 的每个方法(几乎每个方法)以及该方法在不同情况下应返回的不同结果,就需要进行大量设置。

我可以看到,随着我进一步开发,可能会有更多实用程序类调用、大量对象以及对这些模拟实用程序类的大量设置。

【问题讨论】:

    标签: unit-testing tdd


    【解决方案1】:

    规则不是“模拟一切”,而是“让测试变得简单”。如果

    1. 您无法通过合理的努力创建实例(阅读:您需要一个方法调用,但要创建实例,您需要一个工作数据库、一个数据库连接和五个其他类)。
    2. 创建附加类的成本很高。
    3. 其他类返回不稳定的值(如当前时间或数据库中的主键)

    【讨论】:

      【解决方案2】:

      TDD 并不是真正的测试。它的主要好处是帮助您设计其他人可以理解和更改的干净、易于使用的代码。如果它的主要好处是进行测试,那么您将能够在代码之后而不是之前编写测试,效果大致相同。

      如果可以,我建议您不要将它们视为“单元测试”。相反,您可以将您的测试视为如何使用代码的示例,以及说明代码为何有价值的行为描述。

      作为该行为的一部分,您的班级可能想要使用一些协作班级。你可以模拟这些。

      如果您的实用程序类是您的类行为的核心部分,而您的类没有价值或没有它们就没有任何意义,那么不要模拟它们。

      Aaron Digulla 的回答非常好;我会根据这些原则将他的每个答案改写为:

      1. 协作类的行为很复杂,并且独立于您感兴趣的类的行为。

      2. 创建协作班级不是班级的重要方面,也不需要成为班级责任的一部分。

      3. 协作类提供的上下文会改变您的类的行为,因此会举例说明如何使用它以及您可能期望什么样的行为。

      希望这是有道理的!如果你喜欢它,看看 BDD,它使用这种词汇远远超过“测试”。

      【讨论】:

      • "如果没有它们,你的类没有价值,或者它的行为没有意义,那就不要嘲笑它们" - 所以在我获取点之间距离的例子中,距离是关键确定我正在测试的方法的部分功能,你会说“不要模拟它”?
      • 好吧,距离提供了上下文,所以也许。如果你有不同的策略,那么模拟它的好时机 - 例如,如果你可以按距离或成本计算 - 但是我会模拟策略而不仅仅是距离。目前,我将专注于使测试易于阅读和理解。
      【解决方案3】:

      理论上您应该尝试模拟所有依赖项,但实际上这是不可能的。例如。您不会模拟标准库中的基本类。在您的情况下,如果实用程序类仅包含一些基本的辅助方法,我想我不会费心去模拟它。

      如果它比这更复杂或连接到一些外部资源,你必须模拟它。您可以考虑创建一个专用的模拟构建器类,它将为您创建一个标准模拟(定义了一些标准存根等),这样您就可以避免在所有测试类中模拟代码重复。

      【讨论】:

      • 是的。当实用程序连接到外部资源(如 URL)时,您应该模拟它。
      【解决方案4】:

      不,这是不可接受的,因为您不再孤立地测试类,这是单元测试最重要的方面之一。即使该实用程序有自己的一组测试,您也正在使用它对该实用程序的依赖性对其进行测试。为了简化模拟对象的创建,您可以使用模拟框架。以下是一些受欢迎的选择:

      当然,如果这个实用程序类是私有的并且只能在被测类的范围内使用,那么你不需要模拟它。

      【讨论】:

      • 单独测试一个类是不现实的。如果你真的想推动它,你也必须模拟 java.lang.String。
      • @Aaron,同意,当我的意思是隔离时,我的意思是隔离开发人员编写的代码。
      • 我已经在使用起订量,但仍然 - 如果我需要对方法调用的特定响应,我需要进行设置。随着事情变得越来越复杂,设置用例变得越来越长。
      • 这表明您的方法正在执行太多任务,应该拆分/重构以尽可能尊重SRP
      • @pete 你不需要为模拟对象设置所有方法。相反,您可以创建动态模拟并仅设置那些应该在测试类中调用的方法。
      【解决方案5】:

      是的,这是可以接受的。重要的是对 UtilityClass 进行彻底的单元测试,并且能够区分测试失败是因为被测类还是因为 UtilityClass。

      单独测试一个类意味着在受控环境中测试它,在一个可以控制对象行为方式的环境中。
      必须在测试设置中创建太多对象表明环境变得太大,因此没有得到足够的控制。是时候恢复到模拟对象了。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2019-10-15
        • 1970-01-01
        • 2019-06-02
        • 2017-07-10
        • 1970-01-01
        • 1970-01-01
        • 2019-08-19
        相关资源
        最近更新 更多