我应该对私有/受保护方法进行单元测试吗
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对一个方法进行单元测试,该方法具有多个内部调用保护/私有方法?