我经常被那些赞同转向测试驱动开发 (TDD) 的软件开发人员所接近。他们理解,首先描述期望,然后编写代码来满足这些期望是编写软件的最佳方式。他们也同意,首先编写测试不会引入任何额外开销,因为反正他们都必须编写测试。尽管如此,他们仍然发现自己陷入困境,不清楚要测试什么、何时测试以及如何测试。本文将回答这些问题。
首先,一个类比
想象一下,您正在一个团队中工作,该团队被要求制造一辆赛车。目标是交付一款产品,使车队能够驾驶汽车从一个城市(比如俄勒冈州的波特兰)到另一个城市(比如华盛顿州的西雅图)。
您的团队可以通过几种不同的方式来设计和制造这辆汽车。一种方法是手工制作一个独特的、整体式的车辆,其中所有部件都是自制的且紧密耦合的。另一种方法是仅使用预制部件并将它们缝合在一起。这两种极端方法之间还有许多其他排列组合。
假设您的团队选择手工制造赛车的组成部件。汽车需要电池才能运行。为了这个类比的目的,请关注定制的汽车电池。您将如何测试它?
测试策略
测试定制汽车电池的一种方法是聘请一个测试团队,将装有电池的汽车运到波特兰,然后让测试团队驾驶汽车从波特兰到西雅图。如果汽车到达西雅图,您可以确认,是的,汽车电池功能如预期。
测试定制汽车电池的另一种方法是将其安装在汽车中,看看发动机是否转动。如果发动机启动,您可以确认,是的,汽车电池功能如预期。
还有另一种方法是使用电压表并连接正极 (+) 和负极 (-) 端子,看看电压表是否记录到 12.6 至 14.7 伏范围内的电压输出。如果记录到,您可以确认,是的,汽车电池功能如预期。
以上三个假设的例子说明了汽车电池的不同测试方法如何与三类测试策略对齐
- 雇用测试团队驾驶汽车从波特兰到西雅图与系统或端到端测试策略对齐。
- 将电池安装在汽车中并验证发动机是否启动与集成测试策略对齐。
- 测量汽车电池的电压输出以验证其是否在预期范围内与单元测试策略对齐。
TDD 完全是关于单元测试
我希望这些例子为区分单元测试、集成测试和系统端到端测试提供简单的指导原则。
牢记这些指导原则,永远不要在您的 TDD 实践中包含集成测试或系统测试,这一点非常重要。在 TDD 中,预期的结果始终是微观结果。测量汽车电池的电压输出是微观结果的一个很好的例子。汽车电池是一个功能单元,不能轻易分解为几个更小的功能单元。因此,它是编写单元测试(即,描述预期的可测量输出)的完美候选者。
您还可以将您的期望描述为:“我期望在转动钥匙时汽车发动机启动。” 但是,该描述不符合单元测试的条件。为什么?因为汽车的粒度级别不够低。在软件工程术语中,汽车不体现单一职责原则 (SRP)。
当然,虽然您也可以将您的期望描述为:“我期望从波特兰开始旅程的汽车在 x 小时后到达西雅图,”但该描述不符合单元测试的条件。从波特兰到西雅图的汽车旅程的许多方面都可以测量,因此这种端到端描述绝不应成为 TDD 的一部分。
模拟真实条件
在汽车电池的情况下,仅通过使用简单的电压表,您就可以模拟汽车电池的运行环境。您不必花费巨资来提供完整的体验(例如,功能齐全的汽车,从波特兰到西雅图的漫长而危险的旅程)才能确信您的汽车电池确实如预期般运行。
这就是单元测试简单性的魅力所在。它易于模拟,易于测量,易于让练习者确信一切都如预期般工作。
那么是什么使这种魔力成为可能呢?答案很简单——缺乏依赖性。汽车电池不依赖于与汽车相关的任何事物。它也不依赖于与从波特兰到西雅图的公路旅行相关的任何事物。请记住,随着您分解的系统组件变得越来越不依赖于其他组件,您的解决方案将变得越来越可靠。
结论
软件工程的艺术在于将复杂系统分解为小的组成元素的能力。每个单独的元素都必须缩小到尽可能小的表面。一旦您在分解系统的过程中达到这一点,您就可以非常容易地专注于描述您对每个单元输出的期望。您可以通过遵循形式化的模式来做到这一点,在该模式中,您首先描述前提条件(即,假设存在如此这般的值),动作(即,假设如此这般的事件发生),以及结果或后置条件(即,您期望如此这般的值是可测量的)。
5 条评论