有时私有功能只是功能的内部单元,尚待提取。那么为什么不测试它们呢?


9

有时,模块或类的私有函数仅仅是功能的内部单元,尚待提取,可能值得对其进行测试。那么为什么不测试它们呢?如果提取它们,我们在稍后为它们编写测试。那么,当它们仍属于同一文件时,为什么不立即编写测试呢?

展示:

在此处输入图片说明

首先,我写道module_a。现在,我要为此编写测试。我想测试“私有”功能_private_func。我不明白为什么我不为它编写测试,如果以后我仍然可以将其重构为它自己的内部模块,然后为它编写测试。


假设我有一个具有以下功能的模块(它也可以是一个类):

def public_func(a):
    b = _do_stuff(a)
    return _do_more_stuff(b)

_do_stuff并且_do_more_stuff是模块的“私有”功能。

我理解我们只应该测试公共接口,而不是实现细节的想法。但是,这是事情:

_do_stuff_do_more_stuff包含该模块的大部分功能。它们中的每个都可以是不同“内部”模块的公共功能。但是它们尚未进化,并且足够大以至于可以提取为单独的文件。

因此,测试这些功能是正确的,因为它们是功能的重要单元。如果它们作为公共功能位于不同的模块中,我们将对其进行测试。那么,为什么还没有(或曾经)将它们提取到另一个文件时不对其进行测试呢?


2
TDD
t

2
“私有方法有利于单元测试……” “ ...坚持私有方法为我带来了有用,可靠的单元测试增强。相反,减弱“可测试性”的访问限制只会使我晦涩难懂一段测试代码,这是另外在任何轻微的重构被打破永久风险;坦率地说我得到什么看起来很像技术债”
蚊蚋

3
“我应该测试私有功能吗?” 不,永远,永远,永远。
David Arno

2
@DavidArno为什么?什么是测试内部的替代方法?仅集成测试?还是将更多事情公开?(尽管以我的经验,我主要在内部类上测试公共方法,而不是私有方法)
CodesInChaos

1
如果足够重要,需要为其编写测试,则它应该已经在单独的模块中。如果不是,那么您可以使用公共API测试其行为。
Vincent Savard

Answers:


14

测试的需求与公开的需求不同。

不重要的代码无论暴露在何处都需要测试。非公共行为不需要存在,更不用说进行测试了。

这些矛盾的观点可能导致您想要公开每个函数,或者拒绝将代码分解为一个函数,除非它将公开。

这不是答案。乐于创建私人助手功能。通过尽可能使用它的公共接口对其进行测试。

如果通过公共接口进行的测试不能充分发挥私有功能的作用,则私有功能会试图允许这样做。

验证可以帮助缩小私有功能允许的范围。如果不能通过公共接口传递null,则无论如何还是可以抛出异常。

你为什么要?为什么要测试您将永远看不到的东西?因为事情变了。它现在可能是私有的,但稍后会公开。调用代码可能会更改。明确拒绝null的代码使正确的用法和期望的状态清晰可见。

当然null可能没问题。这只是一个例子。但是,如果您期望某些东西,则使期望值变得清晰很有用。

那可能不是您要进行的测试,但是希望您愿意在适当的时候创建私有帮助函数。

测试的愿望是好的,但它不应成为设计公共API的原动力。将公共API设计为易于使用。如果每个功能都是公开的,则可能不会。人们应该可以理解API的使用方法,而无需深入研究代码。不要让这些人想知道这个奇怪的辅助函数是做什么的。

在内部模块中隐藏公共帮助程序功能是在公开帮助程序进行测试时,要尊重对干净API的需求。我不会说这是错误的。您可能正在迈向另一个体系结构层的第一步。但是请通过先使用私有助手功能的公共功能来掌握测试私有助手功能的技巧。这样,您就不会过度使用此替代方法。


我想出一种方法,希望听听您的意见:每当我要测试私有功能时,我都会检查是否可以通过其中一个公共功能对其进行充分测试(包括所有极端情况等)。如果可以的话,我不会专门为此功能编写测试,而只是通过测试使用该功能的公共功能来进行测试。但是,如果我觉得该功能无法通过公共接口进行充分的测试,并且应该对其进行单独的测试,则可以将其提取到内部模块中进行公共测试,并为此编写测试。你怎么看?
阿维夫·科恩

我要说的是,我在问其他在这里回答过的人同样的事情:)我想听听大家的意见。
阿维夫·科恩

再说一次,我不会告诉你不。我担心您在关注如何影响可用性方面一言不发。公共和私人之间的区别不是结构性的。它的用法。如果公共和私人之间的区别是前门和后门,那么您的解决方法是在后院建一个棚子。精细。只要人们不会迷路回到那里。
candied_orange

1
因“如果通过公共接口进行的测试不能充分行使私有功能,则私有功能将尝试允许这样做。”
克里斯·威尔士,

7

简短答案:否

更长的答案:是的,但是通过您班级的公共“ API”

类的私有成员的全部想法是,它们表示应该在代码的“单元”之外不可见的功能,无论您想要定义该单元为多大。在面向对象的代码中,该单元通常最终成为一个类。

您应该设计您的类,以便可以通过输入状态的各种组合来调用所有私有功能。如果您发现没有相对直接的方法可以执行此操作,则可能暗示您的设计需要密切注意。


在澄清问题之后,这仅仅是语义问题。如果有问题的代码可以作为独立的独立单元运行,并且正在像公共代码一样进行测试,那么我看不到不将其移入独立模块的任何好处。目前,它仅能使未来的开发人员(包括您自己,在6个月的时间内)感到困惑,以了解为什么显然是公共代码隐藏在另一个模块中。


嗨,谢谢您的回答:)请重读这个问题,我已经编辑过以澄清问题。
阿维夫·科恩

我想出一种方法,希望听听您的意见:每当我要测试私有功能时,我都会检查是否可以通过其中一个公共功能对其进行充分测试(包括所有极端情况等)。如果可以的话,我不会专门为此功能编写测试,而只是通过测试使用该功能的公共功能来进行测试。但是,如果我觉得该功能无法通过公共接口进行充分的测试,并且应该对其进行单独的测试,则可以将其提取到内部模块中进行公共测试,并为此编写测试。你怎么看?
阿维夫·科恩

我要说的是,我在问其他在这里回答过的人同样的事情:)我想听听大家的意见。
阿维夫·科恩

5

私有函数的全部要点是它们是隐藏的实现细节,可以在不更改公共API的情况下随意更改。对于您的示例代码:

def public_func(a):
    b = _do_stuff(a)
    return _do_more_stuff(b)

如果您有一系列仅使用的测试,public_func则将其重写为:

def public_func(a):
    b = _do_all_the_new_stuff(a)
    return _do_all_the_old_stuff(b)

然后,只要特定值的返回结果a保持不变,那么您的所有测试都会很好。如果返回结果发生更改,则测试将失败,突出显示某些东西已损坏。

这是一件好事:静态公共API;封装良好的内部结构;和强大的测试。

但是,如果您为_do_stuff或编写了测试_do_more_stuff,然后进行了上述更改,那么您现在将遇到很多失败的测试,这不是因为功能发生了变化,而是因为该功能的实现发生了变化。这些测试需要重写才能与新功能一起使用,但是让它们正常工作后,您所知道的就是那些测试与新功能一起使用。您会丢失原始测试,因此不知道的行为是否public_func已更改,因此您的测试基本上是无用的。

这是一件坏事:不断变化的API;暴露的内部工作与测试紧密结合;和脆弱的测试,这些更改会在实现更改后立即更改。

因此,不,请不要测试私有功能。曾经


大卫您好,谢谢您的回答。请重新阅读我的问题,我已进行编辑以澄清问题。
阿维夫·科恩

嗯 测试内部功能没有错。我个人不会编写任何类型的非平凡函数,除非我可以用几个单元测试来介绍它,所以禁止对内部函数进行测试将使我无法编写内部函数,从而使我失去了一种非常有用的技术。API可以并且可以更改;当他们这样做时,您必须更改测试。重构内部函数(及其测试)不会破坏外部函数的测试。那就是进行这些测试的全部重点。总体上不好的建议。
罗伯特·哈维

您真正在争论的是您不应该具有私有功能。
罗伯特·哈维

1
@AvivCohn它们足够大,可以进行测试,在这种情况下,它们足够大,可以获取自己的文件;或它们足够小,您无需单独测试它们。那是什么呢?
2013年

3
@RobertHarvey不,它的论点是“如有必要,将大类分解为松耦合的组件”。如果它们不是真正公开的,那么这可能是实现私有包可见性的好用例。
2013年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.