单元测试私有方法是一个好的实践吗?

15
我在想单元测试私有方法是否是一种好的实践?
通常只应测试公共接口。
然而,在复杂计算过程中,会调用大量不同的私有方法,此时先对私有方法进行单元测试,然后再为公共接口方法创建简单测试会更容易一些。
以音频播放器为例,您有以下函数:
void play(){ ... }
void pause(){ ... }
void seek(time t)
{
    //All Private methods
    checkIfValidTimeRange(...);
    moveToFilePos(...);    
    fillBuffers(...);      
}

通常情况下,我会为以下函数编写单元测试:checkIfValidTimeRange(...)moveToFilePos(...)fillBuffers(...)

但我不确定这样做是否是一个好的实践。


2
尽管做出有意义的事情。如果您的私有方法很复杂,为什么不测试它们呢? - Karoly Horvath
我个人认为,不直接测试私有成员有点像汽车制造商仅通过驾驶汽车来测试化油器。 - Codie CodeMonkey
4个回答

14

这并不是一个好的实践(但这并不意味着你永远不应该这样做),如果可能的话,你应该避免这样做。测试私有方法通常意味着你的设计可能需要改进。让我们快速看一下你的播放器示例:

  • moveToFilePos:听起来更像是执行I / O操作而不是音乐播放器的职责
  • fillBuffers:更多是内存管理器的工作,而不是音乐播放器
  • checkIfValidTimeRange:同样,可能可以将其移出播放器的范围到某个简单的验证类中(似乎这个也可能在其他地方有用)

目前你的音乐播放器承担了I/O、内存管理和其他种种职责。这真的都在它的职责范围内吗?


这个例子只是为了展示一个简单的演示。但是假设我创建了MusicFileIO类和MusicFileBuffer类进行检查,我应该将这些类设置为音乐播放器类的内部类吗?(因为我不想向用户公开它们的存在)。但是如果我将它们设置为内部和私有的,难道我不是回到了最初的问题吗? - Anton
1
@Anton:只要新类得到适当的测试,它们暴露的程度就不重要了。我们正在偏离真正的问题——测试私有方法并不是因为方法是私有的,而是因为这通常意味着设计不良。你不想发现自己处于这样一种情况:更改fillBuffer(“现在缓冲区从其他地方填充!”-匿名客户端)会导致程序中的音乐播放中断。现在几乎没有意义,以后完全没有意义。 - k.m
未经测试的代码会变成“遗留”代码(参见《与遗留代码有效工作》),因此,如果公共接口的实际复杂性使得完全覆盖不可行(即外观模式),在我看来,测试私有方法是两害相权取其轻的选择。 - Txangel

5
如果您的私有方法复杂到需要进行测试,那么很可能缺少一些类来将这些私有方法转换为公共方法。当然,您可以测试私有方法,但是您应该将此需求视为设计中存在问题的提示。

4

在我看来,这是一个非常好的想法,我经常这样做。通常我会创建一个辅助类,使私有方法可以访问并进行测试。

通常测试私有方法更容易一些,因为它们执行的任务非常具体。另一方面,你可能有一个比较复杂的公共方法,这会让测试变得有点困难。所以这肯定简化了单元测试。


0
你的代码库中哪些部分依赖于你的私有方法? 如果有人更改了你依赖的一个方法的工作方式,从而破坏了你的方法,难道不值得知道吗? 测试不仅是为了检查你的方法是否按预期运行,还要检查代码库其他部分的更改是否会破坏你的方法。
因此,除非你的方法只使用语言的基本结构,否则请对其进行测试!

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接