如果 TDD 是针对单元的,那么测试怎么会意外地先通过(作为错误)?
If TDD is for units, how can a test accidentally pass first (as a mistake)?
我读过一份关于 TDD 的研究,其中一个常见问题(开发人员之间的调查)表明他们并没有真正让测试先失败。作者然后声明:
If a new test does not fail, programmers receive an indication that
the production code was not working as they thought it was and a code
revision might be necessary. Another problem that might occur is that
programmers cannot be sure about what made the test pass; nothing
ensures the new code was actually responsible for it. The test
implementation might have been wrong since the beginning.
我想知道,TDD 测试怎么能先通过 (因为生产代码,就像他们提到的那样),如果它是在单元级别?我的意思是,如果所有的东西都被嘲笑(存根……),它应该总是被隔离,因此永远不能真正先通过。
假设您有两个 类 Calculator
和 Formatter
.
Calculator
根据输入计算出一些值,Formatter
将该值转换为字符串显示。
您的FormatterTest
中已经有一些测试:
test_value_is_formatted_as_number
test_empty_is_formatted_as_NA
现在您实现新功能 Show zero values as N/A
。
在 TDD 之后,您将向 Formatter
test_zero_is_formatted_as_NA
添加一个测试,首先检查它,您预计它会失败:
def test_zero_is_formatted_as_NA(self):
assert formatter.format(0) == 'N/A'
但碰巧它通过了,原因是 Formatter
已经这样做了,但是 Calculator
returns 精度有限的浮动零。
def format(value):
if value == 0 or value is None:
return 'N/A'
return format_as_string(value)
所以测试通过了,但是如果你写另一个测试它会失败:
def test_very_small_number_is_treated_as_zero_and_formatted_as_NA(self):
assert formatter.format(0.00000001) == 'N/A'
通常,当某些东西已经实现但系统的另一部分(使用这个已实现的部分)以某种方式限制它时,您描述的情况很容易发生,例如通过更强的先决条件。那么如果你对代码不太了解,你可能会遇到这样的惊喜。
考虑这个例子:
public string ShowScoreEvaluation(byte points)
{
switch(points)
case 3:
return "You are good!";
case 2:
return "Not bad!";
case 1:
return "Quite bad";
case 0:
return "You suck!"
}
//caller code
if (Points>0)
ShowScoreEvaluation(points)
在上面的代码中,调用代码不希望在 Points=0 时调用该方法。也许在该方法的实现过程中,程序员只是在 points=0.
的情况下放了一些东西(作为一个笑话或占位符)
现在想象一下,您加入该项目并收到一个新请求 "When player has 0 points, show an encouraging message blabla"。
您使用 Points=0 编写单元测试并期望长度>0 的字符串...但它并没有失败,尽管您希望如此。
我读过一份关于 TDD 的研究,其中一个常见问题(开发人员之间的调查)表明他们并没有真正让测试先失败。作者然后声明:
If a new test does not fail, programmers receive an indication that the production code was not working as they thought it was and a code revision might be necessary. Another problem that might occur is that programmers cannot be sure about what made the test pass; nothing ensures the new code was actually responsible for it. The test implementation might have been wrong since the beginning.
我想知道,TDD 测试怎么能先通过 (因为生产代码,就像他们提到的那样),如果它是在单元级别?我的意思是,如果所有的东西都被嘲笑(存根……),它应该总是被隔离,因此永远不能真正先通过。
假设您有两个 类 Calculator
和 Formatter
.
Calculator
根据输入计算出一些值,Formatter
将该值转换为字符串显示。
您的FormatterTest
中已经有一些测试:
test_value_is_formatted_as_number
test_empty_is_formatted_as_NA
现在您实现新功能 Show zero values as N/A
。
在 TDD 之后,您将向 Formatter
test_zero_is_formatted_as_NA
添加一个测试,首先检查它,您预计它会失败:
def test_zero_is_formatted_as_NA(self):
assert formatter.format(0) == 'N/A'
但碰巧它通过了,原因是 Formatter
已经这样做了,但是 Calculator
returns 精度有限的浮动零。
def format(value):
if value == 0 or value is None:
return 'N/A'
return format_as_string(value)
所以测试通过了,但是如果你写另一个测试它会失败:
def test_very_small_number_is_treated_as_zero_and_formatted_as_NA(self):
assert formatter.format(0.00000001) == 'N/A'
通常,当某些东西已经实现但系统的另一部分(使用这个已实现的部分)以某种方式限制它时,您描述的情况很容易发生,例如通过更强的先决条件。那么如果你对代码不太了解,你可能会遇到这样的惊喜。
考虑这个例子:
public string ShowScoreEvaluation(byte points)
{
switch(points)
case 3:
return "You are good!";
case 2:
return "Not bad!";
case 1:
return "Quite bad";
case 0:
return "You suck!"
}
//caller code
if (Points>0)
ShowScoreEvaluation(points)
在上面的代码中,调用代码不希望在 Points=0 时调用该方法。也许在该方法的实现过程中,程序员只是在 points=0.
的情况下放了一些东西(作为一个笑话或占位符)现在想象一下,您加入该项目并收到一个新请求 "When player has 0 points, show an encouraging message blabla"。 您使用 Points=0 编写单元测试并期望长度>0 的字符串...但它并没有失败,尽管您希望如此。