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