Python 中的 TDD - 我们应该测试辅助函数吗?
TDD in Python - should we test helper functions?
Python 提出的一个理论问题,因为我们几乎可以访问任何我们想要的东西,即使它被强调为“私有”。
def main_function():
_helper_function_()
...
_other_helper_function()
使用 TDD,您遵循 Red-Green-Refactor 循环。测试现在看起来像这样:
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
但这似乎是一种不好的做法。我是否应该“知道”任何 inner/helper 函数?
我通过将部分移出到这些辅助函数中来重构主要函数,使其更具可读性。所以我重写了测试以实际测试这些较小的部分,并创建了另一个测试,主要功能确实调用了它们。这似乎也适得其反。
另一方面,我不喜欢很多挥之不去的 inner/helper 函数没有专门的单元测试的想法,只有主要函数的快乐路径。我想如果我在重构之前覆盖了原始功能,我的旧测试就足够好了。
此外,如果主要功能中断,这将意味着辅助功能的许多其他测试也会中断。
更好的做法是什么?
The problem is that my main_function
had so much setup steps that I've decided to test the helper functions for those specific cases
太好了,这正是应该发生的事情(测试“驱使”您将整体分解成更容易测试的更小的部分)。
Should I even "know" about any inner/helper functions?
权衡。
是的,模块的部分意义在于它们提供 information hiding,允许您稍后更改代码的执行方式而不影响客户端,包括测试客户端。
但是直接测试内部模块也有好处;测试设计变得更简单,与 无关 细节的耦合更少。每个决定耦合的测试更少,这意味着当您需要更改其中一个时,爆炸半径更小。
我通常的想法是这样的:我应该知道有可测试的内部模块,我可以知道一个外部模块表现得像它耦合到一个内部模块,但我不一定知道外部模块是耦合到内部模块。
assert X.result(A,B) == Y.sort([C,D,E])
如果你仔细看这个,你会发现它暗示 X.result
和 Y.sort
今天有一些共同的要求,但它不一定保证 X.result 调用 Y.sort.
So I've rewritten tests to actually test these smaller parts and created another test that the main function indeed calls them. This also seems counter-productive.
A
有效,B
有效,C
有效,现在你正在为 f(A,B,C)
编写测试......是的,一切顺利侧身.
TDD 的预期结果是“有效的干净代码”(Jeffries);事情的真相是,您可以获得干净的代码,而无需编写世界上的每个测试。
测试在最有可能出现错误的代码中最为重要 - 我们只是将事物连接在一起的直线代码从红-绿-重构循环中受益的程度远不如具有大量条件的代码和分支。
There are two ways of constructing a software design: One way is to make it so simple that there are obviously no deficiencies
对于“简单到明显没有缺陷”的代码段,一套自动化的程序员测试并不是什么大投资。找两个人进行人工审核,然后签字。
太多 private/helper 函数通常是缺少抽象的标志。
也许您应该考虑应用 'Extract class' 重构。此重构将解决您的困惑,因为私有成员最终将成为提取的 class.
的 public 成员
请注意,我在这里并不是建议为每个私人成员创建一个 class,而是要稍微尝试一下模型以找到更好的设计。
Python 提出的一个理论问题,因为我们几乎可以访问任何我们想要的东西,即使它被强调为“私有”。
def main_function():
_helper_function_()
...
_other_helper_function()
使用 TDD,您遵循 Red-Green-Refactor 循环。测试现在看起来像这样:
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
但这似乎是一种不好的做法。我是否应该“知道”任何 inner/helper 函数?
我通过将部分移出到这些辅助函数中来重构主要函数,使其更具可读性。所以我重写了测试以实际测试这些较小的部分,并创建了另一个测试,主要功能确实调用了它们。这似乎也适得其反。
另一方面,我不喜欢很多挥之不去的 inner/helper 函数没有专门的单元测试的想法,只有主要函数的快乐路径。我想如果我在重构之前覆盖了原始功能,我的旧测试就足够好了。
此外,如果主要功能中断,这将意味着辅助功能的许多其他测试也会中断。
更好的做法是什么?
The problem is that my
main_function
had so much setup steps that I've decided to test the helper functions for those specific cases
太好了,这正是应该发生的事情(测试“驱使”您将整体分解成更容易测试的更小的部分)。
Should I even "know" about any inner/helper functions?
权衡。
是的,模块的部分意义在于它们提供 information hiding,允许您稍后更改代码的执行方式而不影响客户端,包括测试客户端。
但是直接测试内部模块也有好处;测试设计变得更简单,与 无关 细节的耦合更少。每个决定耦合的测试更少,这意味着当您需要更改其中一个时,爆炸半径更小。
我通常的想法是这样的:我应该知道有可测试的内部模块,我可以知道一个外部模块表现得像它耦合到一个内部模块,但我不一定知道外部模块是耦合到内部模块。
assert X.result(A,B) == Y.sort([C,D,E])
如果你仔细看这个,你会发现它暗示 X.result
和 Y.sort
今天有一些共同的要求,但它不一定保证 X.result 调用 Y.sort.
So I've rewritten tests to actually test these smaller parts and created another test that the main function indeed calls them. This also seems counter-productive.
A
有效,B
有效,C
有效,现在你正在为 f(A,B,C)
编写测试......是的,一切顺利侧身.
TDD 的预期结果是“有效的干净代码”(Jeffries);事情的真相是,您可以获得干净的代码,而无需编写世界上的每个测试。
测试在最有可能出现错误的代码中最为重要 - 我们只是将事物连接在一起的直线代码从红-绿-重构循环中受益的程度远不如具有大量条件的代码和分支。
There are two ways of constructing a software design: One way is to make it so simple that there are obviously no deficiencies
对于“简单到明显没有缺陷”的代码段,一套自动化的程序员测试并不是什么大投资。找两个人进行人工审核,然后签字。
太多 private/helper 函数通常是缺少抽象的标志。
也许您应该考虑应用 'Extract class' 重构。此重构将解决您的困惑,因为私有成员最终将成为提取的 class.
的 public 成员请注意,我在这里并不是建议为每个私人成员创建一个 class,而是要稍微尝试一下模型以找到更好的设计。