【问题标题】:Is it a good practice to use package private methods in order to facilitate unit tests?使用包私有方法来促进单元测试是一种好习惯吗?
【发布时间】:2015-05-06 05:14:55
【问题描述】:

有时我发现自己在这样的情况下,如果我将某些方法的可见性从私有更改为包私有,以便于单元测试模拟、断言......

一个例子是这样的

假设我有一个包含 4 个属性 X、Y、Z 和 R 的对象 A,其中 X、Y 和 Z 是集合,R 是每个集合的不同元素之间的关系,例如,一个关系将由一个元素组成X ,Y 的一个元素和 Z 的一个元素。对象 A 不允许直接访问 X、Y、Z 或 R,而是提供了丰富的 API,允许您在 X、Y 和 Z 上创建新元素,也允许您将这些元素混合到新的 R 元素中。对于单元测试,拥有公共 getX()、公共 getY()、公共 getZ() 和公共 getR() 方法会非常方便,因此我可以在每次调用对象的内部时做出准确的断言对象 API。然而,暴露 X、Y 和 Z 是我想要防止的,这就是为什么从一开始对象就将这些元素设为私有并且仅通过使用其 API 提供对它们的间接访问。然而,提供包私有方法 getX()、getY()、getZ() 和 getR() 是否有意义,以便至少形成单元测试我可以轻松检查对象的内部状态是否是预期的?

缺点当然是增加了方法的可见性,而且考虑到这种方法是私有的,这是有充分理由的,感觉有点奇怪。

当然我可以使用反射来达到同样的效果,但感觉更脏。

所以问题是,这是一种好的做法还是坏的做法?是代码味道吗?它发生在别人身上吗?有没有更好的技术呢?

【问题讨论】:

标签: java unit-testing encapsulation


【解决方案1】:

一般建议是仅测试该类的公共 API(调用私有 API 并同时进行测试)。否则,如果要重构您的内部 API,您将需要重构大部分测试。

【讨论】:

    【解决方案2】:

    如果 A 管理 X、Y、Z 和 R,听起来 A 做的事情太多了。

    也许一个好主意是重构您的代码,使 R 成为一个单独的类,将 X、Y 和 Z 作为输入参数。如果您愿意,可以将该类设为私有包。无论哪种方式,您都可以通过提供不同的 X、Y 和 Z 来直接测试 R。

    【讨论】:

      【解决方案3】:

      如果您想测试私有方法,您有几个选择:

      1. 依赖注入

      如果您确实需要测试私有方法,则该方法很可能实际上应该是另一个类的公共方法(代码异味)。这意味着如果您在“A”类中有私有方法“m”,则可以考虑将“m”作为公共方法移动'B' 类中的方法,然后将 'B' 注入 'A'

      这种技术很常见,而且在大多数情况下都能奏效。不过,只有将代码的重要部分从原始类 ('A') 移动到新类 ('B') 时,它才有意义。

      此解决方案对 Mockito 友好。

      1. 测试双重继承

      将类 'A' 中的私有方法 'm' 的可见性更改为受保护。然后在扩展 'A'test 文件夹 中创建一个类 'B'。最后,在 'B' 中创建一个公共方法 'pm',其签名与 'm' 相同,只是调用 'm '。这样,您可以通过 '中的 'pm' 访问 'A' 中不可见的 'm' 方法B'

      这个解决方案也是 Mockito 友好的。

      1. 时髦的

      使用允许调用私有方法的 JVM 动态语言,例如 Groovy。

      1. 反射

      使用反射获取私有方法,然后更改其可见性。见this

      1. 包可见性

      将方法声明为包(无可见性约束)并在同一包中声明测试类。我个人不喜欢使用这个,因为我找不到优雅的 Java 包可见性。

      【讨论】:

      • +1 表示“如果您真的需要测试私有方法,那么该方法很可能实际上应该是另一个类的公共方法”
      【解决方案4】:

      对于单元测试,在测试和模拟私有、静态方法和构造函数时可能会遇到一些问题。因此,您可以选择使用以下两种解决方案之一:

      1. 不要使用私有方法,让它们包可见。也不要使用静态方法并将它们放在单例类中。

      2. 使用类似 powermock 的模拟库来执行此类测试。它还可以模拟构造函数和静态方法。

      解决方案是好是坏,我想没有,这取决于项目的策略,以及对它们进行测试的决定。

      【讨论】:

        【解决方案5】:

        一般来说,好的做法是暴露内部逻辑。相反,您必须使您的类可配置。例如,如果您的类需要其他组件,比如 HttpComponent 等,请尝试使用不同的依赖注入技术来提供这些依赖。然后在测试中,您可以模拟这些依赖项并验证这些模拟。

        在您的情况下,这取决于上下文。大多数时候,您将测试私有函数作为测试公共函数的一部分。因此,您测试不同情况下的公共行为,如果全部通过,则意味着通过该公共函数调用的私有函数也可以工作。

        【讨论】:

          猜你喜欢
          • 2012-03-01
          • 2010-11-25
          • 2017-03-09
          • 2011-01-06
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2017-06-14
          • 2012-10-23
          相关资源
          最近更新 更多