我应该对私有/受保护方法进行单元测试吗

Posted

技术标签:

【中文标题】我应该对私有/受保护方法进行单元测试吗【英文标题】:Should I unittest private/protected method 【发布时间】:2015-02-26 08:47:21 【问题描述】:

这实际上与语言无关。但我会在 python 中给你上下文。

我有这个父类

class Mammal(object):
    def __init__(self):
        """ do some work """

    def eat(self, food):
        """Eat the food"""
        way_to_eat = self._eating_method()
        self._consume(food)

    def _eating_method(self):
        """Template method"""

    def _consume(self, food):
        """Template method"""

这里eat 是唯一的公共方法,而_consume_eating_method 实际上是受保护的方法,将由子类实现。

如果你只写了Mammal 类,你会测试什么?

显然所有 4 种方法。

现在让我们介绍一个孩子

class Tiger(Mammal):    
    def _eating_method(self):
        """Template method"""

    def _consume(self, food):
        """Template method"""

看看这堂课。它只有 2 个 protected 方法。

我应该测试Tiger 的所有 4 个方法(包括 2 个继承的)还是只测试引入的更改(仅覆盖 2 个方法)?

理想情况是什么?

【问题讨论】:

这取决于。通常,eat 方法会使用_eating_method_consume 的所有可能结果进行测试。所以只需要测试改变的方法。 【参考方案1】:

我的处理方法是:

创建Mammal 的最小可测试子类,它提供两个受保护方法的最小实现,允许您对公共方法的行为进行单元测试。 为每个子类编写单独的单元测试,再次测试Mammal 上的公共方法,但断言特定于该子类的行为。

这应该以最少的测试为您提供必要的测试覆盖率。

另一种方法是仅测试子类,并且在其中一个子类上的单元测试还断言特定于Mammal 的功能。这避免了创建特定测试子类的需要,但是有两个缺点:

您不再孤立地测试Mammal,因此对Mammal 特定代码的测试很容易因为子类中的问题而失败。 对于其他人来说,Mammal 的属性是如何以及在何处被测试的可能不太明显。

【讨论】:

【参考方案2】:

从理论的角度来看,您只需要测试可实例化类的公共方法(在标准 OOP 语言中)。测试内部行为没有意义,因为您想要的只是“该输入的哪个输出”(对于特定方法或整个类)。你应该尽量尊重它,因为它迫使你提出一些关于你的类的encapsulation 和提供的接口的问题,这可能对你的架构起决定性作用。

从实用的角度来看,有时您可能会拥有一些没有实现的具体子类的抽象辅助类,或者一个抽象类分解了 90% 以上的子类,而且很难实现在不插入受保护方法的情况下测试输出。在这种情况下,您可以mock 一个子类。

在您的简单示例中,我建议您仅测试类 Tiger(并且仅测试公共方法 eat)。

只是给想TDD 的人的注释。在 TDD 中,您不应该在类 Tiger 之前开始编写类 Mammal,因为 Mammal 应该是重构阶段的结果。因此,您当然不会对 Mammal 进行任何特定测试。

【讨论】:

是的,你可以测试这个方法(唯一一个公开的)。【参考方案3】:

在测试时,您应该关注代码的外部行为,而不是实现细节。我不知道上下文,所以我将在这里做一些巨大的假设。

您真的关心(可能是抽象的)哺乳动物超类的行为吗?重用-继承-继承关系那么重要吗?如果您决定将继承关系替换为基于组合的策略怎么办?

我会专注于测试你真正关心的类的行为:“老虎会像预期的那样吃东西吗?”而不是测试一些仅为代码重用而引入的抽象超类。

【讨论】:

以上是关于我应该对私有/受保护方法进行单元测试吗的主要内容,如果未能解决你的问题,请参考以下文章

如何通过phpunit对一个方法进行单元测试,该方法具有多个内部调用保护/私有方法?

是否可以为具有 1)返回类型 void、2)访问说明符私有或受保护的方法编写单元测试?

如何在 C++ 中对受保护的方法进行单元测试?

受保护插槽的单元测试[关闭]

无法在对受保护方法进行单元测试的适当方法之间做出决定

C#:在密封类中模拟和测试受保护(或私有)方法——方法