【发布时间】:2014-04-12 18:30:55
【问题描述】:
我说的是鲍勃叔叔的TDD规则:
- 您不得编写任何生产代码,除非是为了通过失败的单元测试。
- 不允许您编写任何足以导致失败的单元测试;编译失败就是失败。
- 您编写的生产代码不得超过足以通过一个失败的单元测试的数量。
我的问题是,如果您希望创建一个可以生成多个结果的功能,并且在第一次迭代中您实现的代码可以满足所有场景,会发生什么?
我曾经写过这样的代码,因为这是我首先想到的唯一解决方案。
我会说我没有违反这 3 条规则中的任何一条。
- 我编写了一个测试失败条件最少的测试。
- 然后我用足够的代码实现了该功能以通过测试(这是我想出的唯一解决方案;所以我会说我可能编写的代码最少)。
- 然后我写了下一个测试发现它已经通过了。
现在规则呢?
即使该功能是 soooper 重要的功能,我是否也不允许编写此测试?还是我应该回滚并重新开始?
我还要提一下,这个方法不能根据输入的结果或数据进行重构。现在我能想到的示例情况有点愚蠢,但请多多包涵。看看这个例子:
我想创建一个添加数字的方法,这几乎是我能做的。白一个失败的测试:
public function it_can_add_numbers()
{
$this->add(2, 3)->shouldReturn(5);
}
然后让它通过:
public function add($numberOne, $numberTwo)
{
return $numberOne + $numberTwo;
}
现在有人可能会争辩说我应该在第一次迭代中返回 5,因为这足以通过测试并引入回归,但这不是一个实际问题,所以请耐心等待,假设这是唯一的可以想到的解决方案。
现在我的公司希望我确保他们能够将 12 和 13 相加,因为这些是我们会使用很多时间的内部神奇数字。我继续编写另一个测试,因为这是我应该验证功能的方式。
public function it_can_add_twelve_and_thirteen()
{
$this->add(12, 13)->shouldReturn(25);
}
原来测试已经通过了。
在这一点上,我可以选择不写测试,但是如果以后有人对实际代码进行更改并完成它会怎样
public function add($numberOne, $numberTwo)
{
return 5;
}
测试仍会通过,但该功能不存在。
那么,当您在进行改进之前无法立即考虑在第一次迭代中引入可能存在的缺陷时,该怎么办?我应该把它留在这里,等待有人过来把它搞砸吗?我应该把这个案例留给回归测试吗?
【问题讨论】:
-
这里的错误是假设你应该始终严格和盲目地遵循这三个规则;)真正的规则是“当你认为它有价值时写一个测试”。
-
我不止一次看到项目惨遭失败,只是因为一些经理盲目地采用了 tdd。很多时候,单元测试和测试覆盖率都被错误地用于覆盖没人愿意解决的实际问题
-
@OliCharlesworth 谢谢。这正是我想听到的,我个人也相信这一点,但我仍然看到很多人提倡规则。一些人认为,严格遵循它们最终会更好地塑造代码库。
-
@Gufran:同意。这并不是说 TDD 背后的概念没有用或有趣,只是它们不是灵丹妙药。
标签: unit-testing tdd