我已经阅读了这篇关于如何测试私有方法的文章。我通常不测试它们,因为我总是认为只测试将从对象外部调用的公共方法更快。你测试私有方法吗?我应该一直测试它们吗?
当前回答
I understand the point of view where private methods are considered as implementations details and then don't have to be tested. And I would stick with this rule if we had to develop outside of the object only. But us, are we some kind of restricted developers who are developing only outside of objects, calling only their public methods? Or are we actually also developing that object? As we are not bound to program outside objects, we will probably have to call those private methods into new public ones we are developing. Wouldn't it be great to know that the private method resist against all odds?
我知道有些人会回答说,如果我们正在开发另一个公共方法到那个对象中,那么这个方法应该被测试,就是这样(私有方法可以在没有测试的情况下继续存在)。但这也适用于一个对象的任何公共方法:当开发一个web应用程序时,一个对象的所有公共方法都是从控制器方法调用的,因此可以认为是控制器的实现细节。
那么为什么我们要单元测试对象呢?因为这是非常困难的,不是说不可能,以确保我们正在测试控制器的方法与适当的输入将触发底层代码的所有分支。换句话说,我们在堆栈中的位置越高,测试所有行为就越困难。私有方法也是如此。
对我来说,私人方法和公共方法之间的界限是测试时的心理标准。对我来说更重要的标准是:
该方法是否在不同的地方被多次调用? 这种方法是否复杂到需要测试?
其他回答
如果私有方法定义良好(即,它有一个可测试的函数,并且不会随着时间的推移而改变),那么是的。我在有意义的地方测试所有可测试的东西。
例如,加密库可能会隐藏这样一个事实,即它使用一次仅加密8个字节的私有方法执行块加密。我会为此写一个单元测试——它并不意味着要改变,即使它是隐藏的,如果它真的坏了(例如,由于未来的性能增强),那么我想知道是私有函数坏了,而不仅仅是某个公共函数坏了。
它加快了以后的调试。
亚当
我看到很多人都有同样的想法:在公共层面上进行测试。但这不正是我们的QA团队所做的吗?他们测试输入和预期输出。如果作为开发人员,我们只测试公共方法,那么我们只是在重做QA的工作,而不是通过“单元测试”来增加任何价值。
对于从测试中调用什么api,公共和私有并不是一个有用的区分,方法和类也不是。大多数可测试单元在一个上下文中是可见的,但在其他上下文中是隐藏的。
重要的是覆盖范围和成本。您需要最小化成本,同时实现项目的覆盖目标(行、分支、路径、块、方法、类、等价类、用例……)不管团队怎么决定)。
因此,使用工具来确保覆盖率,并设计您的测试以减少成本(短期和长期)。
不要让测试变得过于昂贵。 如果只测试公共入口是最便宜的,那就这样做。 如果测试私有方法成本最低,那就这么做。
随着您的经验越来越丰富,您将能够更好地预测何时值得重构以避免测试维护的长期成本。
我认为最好只测试对象的公共接口。从外部世界的角度来看,只有公共接口的行为是重要的,这是您的单元测试应该指向的方向。
一旦你为一个对象编写了一些可靠的单元测试,你就不希望因为接口背后的实现改变了而不得不回去修改那些测试。在这种情况下,您已经破坏了单元测试的一致性。
“我应该测试私有方法吗?”的答案是“有时.......”。通常情况下,您应该针对类的接口进行测试。
其中一个原因是您不需要对一个特性进行双重覆盖。 另一个原因是,如果您更改了私有方法,则必须为它们更新每个测试,即使对象的接口根本没有更改。
这里有一个例子:
class Thing
def some_string
one + two
end
private
def one
'aaaa'
end
def two
'bbbb'
end
end
class RefactoredThing
def some_string
one + one_a + two + two_b
end
private
def one
'aa'
end
def one_a
'aa'
end
def two
'bb'
end
def two_b
'bb'
end
end
在RefactoredThing中,你现在有5个测试,其中2个你必须为重构而更新,但你的对象的功能实际上没有改变。所以让我们假设事情比这更复杂,你有一些方法来定义输出的顺序,比如:
def some_string_positioner
if some case
elsif other case
elsif other case
elsif other case
else one more case
end
end
这不应该由外部用户来运行,但是您的封装类可能太笨重了,无法一遍又一遍地运行这么多逻辑。在这种情况下,您可能更愿意将其提取到一个单独的类中,为该类提供一个接口并对其进行测试。
最后,假设你的主对象非常重,方法非常小你需要确保输出是正确的。你会想,“我必须测试这个私有方法!”也许你可以通过传入一些繁重的工作作为初始化参数使你的对象更轻?然后你可以放一些更轻的东西进去测试。