我已经开始使用 TDD。如前一个问题所述,最大的困难是处理界面更改。随着需求的变化,您如何减少对测试用例的影响?
9 回答
更改接口需要更新使用该接口的代码。在这方面,测试代码与非测试代码没有任何不同。不可避免的是,该接口的测试需要更改。
通常,当接口更改时,您会发现“太多”测试中断,即对大部分不相关功能的测试结果依赖于该接口。这可能表明您的测试过于广泛,需要重构。发生这种情况的方式有很多种,但这里有一个示例,希望能展示总体思路和特定案例。
例如,如果构造 Account 对象的方式发生了变化,这需要更新您的 Order 类的全部或大部分测试,那么就会出现问题。大多数 Order 单元测试可能并不关心帐户是如何创建的,因此重构测试如下:
def test_add_item_to_order(self):
acct = Account('Joe', 'Bloggs')
shipping_addr = Address('123 Elm St', 'etc' 'etc')
order = Order(acct, shipping_addr)
item = OrderItem('Purple Widget')
order.addItem(item)
self.assertEquals([item], order.items)
对此:
def make_order(self):
acct = Account('Joe', 'Bloggs')
shipping_addr = Address('123 Elm St', 'etc' 'etc')
return Order(acct, shipping_addr)
def make_order_item(self):
return OrderItem('Purple Widget')
def test_add_item_to_order(self):
order = self.make_order()
item = self.make_order_item()
order.addItem(item)
self.assertEquals([item], order.items)
这个特定的模式是一个Creation Method。
这里的一个优点是您的 Order 测试方法与 Accounts 和 Addresses 的创建方式是隔离的;如果这些接口发生更改,您只有一个地方可以更改,而不是碰巧使用帐户和地址的每个测试。
简而言之:测试也是代码,并且像所有代码一样,有时它们需要重构。
我认为这是接口使用过多的流行论点的原因之一。
然而,我不同意。
当需求改变时——你的测试也应该改变。对?我的意思是,如果您编写测试的标准不再有效,那么您应该重写或取消该测试。
我希望这会有所帮助,但我想我可能误解了你的问题。
会有影响。您只需要接受更改接口将需要时间首先更改关联的测试用例。没有办法解决这个问题。
但是,您考虑到以后不再尝试在此界面中找到难以捉摸的错误并且在发布周不修复该错误来节省时间,这是完全值得的。
在 TDD 中,您的测试不是测试。它们是可执行的规范。IOW:它们是您要求的可执行编码。永远记住这一点。
现在,突然变得很明显:如果你的需求改变了,测试也必须改变!这就是 TDD 的全部意义所在!
如果你在做瀑布,你将不得不改变你的规范文档。在 TDD 中,您必须这样做,只是您的规范不是用 Word 编写的,而是用 xUnit 编写的。
“我们应该做些什么来防止我们的代码和测试依赖于需求?似乎没什么。每次当需求改变时,我们都必须改变我们的代码和测试。但也许我们可以简化我们的工作?是的,我们可以。关键原则是: 封装可能更改的代码。”
http://dmitry-nikolaev.blogspot.com/2009/05/atch-your-changes.html
在为新接口编写代码之前编写测试。
如果您遵循“测试优先”方法,理论上接口更改对您的测试代码应该没有影响。毕竟,当您需要更改接口时,您首先要更改测试用例以匹配需求,然后继续更改接口/实现,直到测试通过。
当接口发生变化时,您应该期望测试会中断。如果太多的测试中断,这意味着你的系统耦合太紧,太多的东西依赖于那个接口。您应该期望一些测试会中断,但不会很多。
让测试中断是一件好事,代码中的任何更改都应该中断测试。
如果需求发生变化,那么您的测试应该是首先发生变化的事情,而不是接口。
我会首先在第一个适当的测试中修改界面设计,更新界面以通过新的测试。更新接口以通过测试后,您应该会看到其他测试中断(因为它们将使用过时的接口)。
这应该是用新的界面设计更新剩余的失败测试以使它们再次通过的问题。
以测试驱动的方式更新接口将确保更改实际上是必要的,并且是可测试的。