这是Python中出现的一个理论问题,因为我们几乎可以访问任何我们想要访问的内容,即使它被强调为"隐私"。
def main_function():
_helper_function_()
...
_other_helper_function()
使用TDD,您遵循红-绿-重构周期。一个测试现在看起来像这样:
def test_main_function_for_something_only_helper_function_does():
# tedious setup
...
main_function()
assert something
问题是我的main_function
有这么多的设置步骤,我决定测试这些特定情况下的辅助函数:
from main_package import _helper_function
def test_helper_function_works_for_this_specific_input():
# no tedious setup
...
_helper_function_(some_input)
assert helper function does exactly something I expect
但这似乎是一个不好的做法。我应该"知道"吗?关于任何内部/辅助函数?
我重构了main函数,通过将部分移到这些辅助函数中使其更具可读性。所以我重写了测试来测试这些较小的部分,并创建了另一个测试,主函数确实调用了它们。这似乎也适得其反。
另一方面,我不喜欢大量的内部/辅助函数没有专门的单元测试,只有主函数的快乐路径的想法。我想,如果我在重构之前覆盖原始功能,我的旧测试就会足够好。
另外,如果主函数崩溃了,这将意味着许多辅助函数的额外测试也会崩溃。
更好的做法是什么?
问题是我的
main_function
有这么多的设置步骤,我决定测试这些特定情况下的辅助函数
太好了,这正是应该发生的事情(测试"驾驶";您需要将整体分解为更容易测试的小块)。
我应该"知道"吗?关于任何内部/辅助函数?
权衡。
是的,模块的部分意义在于它们提供了信息隐藏,允许您稍后在不影响客户端(包括测试客户端)的情况下更改代码的工作方式。
但是直接测试内部模块也有好处;测试设计变得更简单,与不相关的细节的耦合更少。每个决定对应的测试更少,这意味着当你需要改变其中一个时,爆炸半径更小。
我通常的想法是这样的:我应该知道有可测试的内部模块,我可以知道一个外部模块的行为就像它耦合到一个内部模块,但我不应该知道外部模块是耦合到内部模块。
assert X.result(A,B) == Y.sort([C,D,E])
如果你斜视这个,你会发现它暗示X.result
和Y.sort
今天有一些共同的需求,但它并不一定保证X.result调用Y.sort.
所以我重写了测试,以实际测试这些较小的部分,并创建了另一个测试,主函数确实调用了它们。这似乎也适得其反。
A
工作,B
工作,C
工作,现在你在这里写一个测试f(A,B,C)
....是啊,世事无常。
TDD的期望结果是"干净的代码可以工作";(Jeffries);事情的真相是,你可以得到干净的代码,而不必编写世界上所有的测试。
测试在最有可能出现错误的代码中是最重要的——我们只是把东西连接在一起的直线代码,从红-绿-重构循环中获得的好处远不如那些有很多条件和分支的代码。
构建软件设计有两种方法:一种方法是使它如此简单,以至于明显没有缺陷
对于"如此简单以至于明显没有缺陷"的代码部分,一套自动化的程序员测试并不是一项伟大的投资。让两个人执行手动评审,并在上面签字。
私有/辅助函数太多往往是缺少抽象的标志。
也许你应该考虑应用"提取类"重构。此重构将解决您的困惑,因为私有成员最终将成为提取类的公共成员。
请不要,我不是建议在这里为每个私有成员创建一个类,而是稍微摆弄一下模型以找到更好的设计。