【问题标题】:Unit test helper methods?单元测试辅助方法?
【发布时间】:2010-12-24 02:20:32
【问题描述】:

我有一些以前有大量方法的类,所以我将这个方法的工作细分为“辅助”方法。

这些辅助方法被声明为private 以强制封装 - 但是我想对大型公共方法进行单元测试。是否也可以对辅助方法进行单元测试,就好像其中一个方法失败了,调用它的公共方法也会失败,这样我们就可以确定它失败的原因?

另外,为了使用模拟对象测试这些,我需要将它们的可见性从私有更改为受保护,这是否可取?

【问题讨论】:

    标签: java unit-testing junit jmock helpermethods


    【解决方案1】:

    一种方法是省略private 并将测试放在同一个包中。然后测试可以调用内部方法,但没有其他人(= 包外)可以。

    此外,失败的内部方法应该会产生错误消息,以便于解决问题。当您将代码投入生产时,您会看到比测试更少的内容,并且您将承受很大的压力来快速解决问题。所以在这里花一分钟可以为你节省一小时后你的老板坐在你的脖子上。

    【讨论】:

    • 与其将它们封装为私有,我更喜欢保护辅助方法,并在 JUnit 测试中从匿名子类对其进行测试。
    • @rsp:这是我以前从未想过的绝妙解决方案。它应该是它自己的答案,所以我可以投票! :)
    【解决方案2】:

    这听起来像是你有错误的问题。你所描述的类似于创建一个子“单元测试”,这让我相信你的单元测试毕竟是在测试一个单元。

    这并不是对您正在尝试做的事情的批评:从“我们今天所处的位置”到“其他明显更好的地方”是一个成功的举措。但是,建议您退后一步来评估一下自己所处的位置 - 了解您当前的情况与某些柏拉图式的理想有何不同有助于展示新的可能性。

    这里有很多关于确定辅助方法范围的建议。另一种可能性是审查实现以确定是否存在潜伏在当前实现中的帮助程序类。创建一个新类和一套测试来练习它总是可以接受的。

    请注意,这种方法使您免于重构:您可以在不更改测试套件的情况下更改实现(因为即使帮助对象不再是生产实现的一部分,帮助对象的单元测试也会继续通过),并且您获得了实现及其测试的干净打包(用例:您决定 bozo-sort 是错误的实现,不应再使用。如果 bozo-sort 实现是孤立的,那么您只需将其删除并它的测试。但是当 bozo-sort 实现的测试与所有其他测试纠缠在一起时,就会涉及到更多的思考)。

    这也可能有助于查看为什么要对代码进行单元测试。如果其中一个原因是“使重构安全”,那么您不想编写将您锁定在实现中的测试。

    【讨论】:

      【解决方案3】:

      如果你的类真的那么大,那么听起来你应该打破辅助对象,而不仅仅是辅助方法(尽管提取方法通常是一个步骤方式)。一旦你这样做了,你的旧类变得更简单,更容易测试(也许使用模拟,也许不是),你可以直接在新的支持类上测试方法。

      我的偏好是通过对象的公共 API 进行测试。如果这太难了,那就暗示该对象应该被分解。

      【讨论】:

      • “如果这太难了,那就暗示应该分解对象。”投你一票。
      【解决方案4】:

      如果您想测试 Helper 方法,您可以将它们从私有更改,但您可以考虑这样做。

      您不应该对实现的私有细节进行单元测试,主要是因为它可能会因重构和“破坏”您的测试而发生变化。

      【讨论】:

        【解决方案5】:

        我对这里的一些答案感到非常震惊。

        本质上有些人在说“不要测试私有代码,因为这违反了 TDD 范式”

        测试该死的代码。做任何你需要做的事情,以确保它完全按照它应该的方式工作。

        就个人而言,我会将方法设置为受保护或默认,编写测试,运行测试,并在成功后恢复为私有。此时,我会将相关测试注释掉,并在其上方留下一个指令块:

        /** 对不起,我继承了一个烂摊子... * 如果您需要测试这些方法,请在源代码中公开它们并取消注释以下 * 行 */

        但绝对不要让严格遵守开发方法妨碍改进代码。

        【讨论】:

        • 你的建议基本上是教条的——你必须单独测试每个方法,否则它违反了 TDD 范式。废话。辅助方法可以只是一段普通代码 - 如果需要,您的代码覆盖工具可以验证公共方法的单元测试是否涵盖了所有这些。
        • 阿门 @M1EK,我只需要发布一个答案,因为没有人提到代码覆盖率!
        • 原始海报表达了单独测试这些方法的愿望。 “......也测试辅助方法,就好像其中一个方法失败了调用它的公共方法也会失败 - 但这样我们可以确定它失败的原因。”
        • 我认为我们在这里谈论的是两件不同的事情。在我的世界里,这样的测试一直自动运行。我无法忍受根据指令更改代码。你的世界显然是不同的。
        【解决方案6】:

        这是我想说继续打破规则的情况之一。

        如果你是从头开始设计类,你肯定不希望辅助方法自己进行单元测试,但是......因为你正在重构一个现有的类,为了使确保你没有破坏任何东西。

        使它们受到保护可以让您自己测试助手,以确保它们在您将逻辑从大毛球方法中提取出来时仍然具有您所期望的行为,并允许您将它们存根并返回固定响应这样您就可以确保要重构的大方法对于辅助方法的某些结果的行为符合预期。

        但此时您还没有完成。拆分方法并不能真正解决问题的根源。现在您已经分解了方法和一组(有点不正统的)测试,可以准确地向您展示所有逻辑的作用,您可以重新检查整个类并尝试找出该方法的原因一开始就这么大。很可能您的整个班级还需要分解为具有不同职责的较小单元,这样在不违反任何规则的情况下更容易测试。

        【讨论】:

          【解决方案7】:

          你基本上有两个选择:

          1. 将辅助方法的范围从私有增加到默认。然后您可以测试这些方法(假设测试类与测试对象在同一个包中)。这提高了类的可测试性,但你牺牲了一些封装

          2. 让一切保持原样。这将阻止您编写非常细粒度的测试,但不需要您牺牲任何封装。

          就我个人而言,我会选择 (2),因为您不应该真的需要测试私有方法。类应该通过它的公共接口进行测试(这反过来又会调用私有方法。测试私有方法可能会导致脆弱的测试,即当只有类的内部行为发生变化时测试会失败。

          还有第三种选择(我不愿提及):使用反射(或其他一些巫术)在您的测试类中调用私有方法。这具有 (1) 的缺点以及反射代码固有的缺点(例如绕过类型检查并且难以阅读)

          【讨论】:

          • 还可以使用在测试用例中定义的调用受保护方法的子类,因此测试类不需要与实现在同一个包中
          【解决方案8】:

          正如 Don 和 Dror 所说,将方法公开以便您可以为它们创建单元测试会破坏封装。然后,您将自己绑定到特定的实现。通过将它们公开,您向全世界声明这些方法是已发布接口的一部分,因此它们的规范是锁定的。

          就个人而言,我会寻求更实用的解决方案:将它们保密,不要编写单元测试。如果您遇到公共方法失败的情况,并且您无法弄清楚原因,但您认为这可能是您的私有方法之一的问题,那么暂时将它们公开,编写单元测试,调试,当您'完成,再次将它们设为私有并注释掉单元测试。

          【讨论】:

            【解决方案9】:

            您可以通过编写单元测试来对这些辅助方法进行单元测试,以执行代码的这些部分。

            您在此类中定义公共 API 是有原因的,对吧?测试一下。如果它有效,则该类有效。

            使用代码覆盖工具来帮助您了解该类的足够部分是否正在测试,如果没有,请编写更多单元测试来运行公共 API 并触及那些没有被覆盖的角落。

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2011-06-20
              • 2013-03-06
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多