TDD 的简单概念是在编写新代码之前(开发之前)编写和纠正失败的测试。这有助于避免代码重复,因为我们一次只编写少量代码以便通过测试。(测试只不过是我们需要测试以满足它们的需求条件)。测试驱动开发是在实际开发应用程序之前开发和运行自动化测试的过程。
TDD的主要目标是使代码更清晰、简单和无bug。
测试驱动开发从设计和开发应用程序的每个小功能的测试开始。在TDD方法中,首先,开发测试来指定和验证代码将做什么。
在正常测试过程中,首先生成代码,然后进行测试。测试可能会失败,因为测试甚至在开发之前就已经开发出来了。为了通过测试,开发团队必须开发和重构代码。重构代码意味着更改某些代码而不影响其行为。
下面的步骤说明了如何进行TDD测试:
- Add a test.
- Run all tests and see if any new test fails.
- Write some code.
- Run tests and Refactor code.
- Repeat.
TDD 生命周期的定义
- 编写测试
- 运行测试
- 修改代码保持正确,重构( Refactor)
- 重复上面的过程
关于TDD的一些认识:
- TDD 既不是测试也不是设计
- TDD 并不是意味着 “编写一些测试,然后构建系统(编码)使这些测试执行通过”
- TDD 并不是意味着 “多做一些测试”
TDD vs. 传统测试(Traditional Testing)
TDD方法主要是确保你的源代码被彻底测试。
- 通过传统测试,成功的测试发现了一个或多个缺陷。它与TDD相同。当测试失败时,你已经取得了进展,因为你知道你需要解决这个问题。
- TDD确保你的系统实际上满足它定义的需求。它有助于建立你对系统的信心。
- 在TDD中,更多的注意力集中在验证测试是否正常工作的生产代码上。在传统测试中,更多关注的是测试用例设计。测试是否将显示应用程序的适当/不正确执行以满足需求。
- 在TDD中,实现100%覆盖测试。与传统测试不同,测试每一行代码。
- 传统测试与TDD的结合导致了对系统进行测试的重要性,而不是对系统的完善。
- 在敏捷建模(AM)中,你应该“有目的地测试”。你应该知道为什么你在测试什么,需要测试什么级别。
什么是 Acceptance TDD 和 Developer TDD
TDD 有两种类型:
Acceptance TDD (ATDD):用ATDD编写一个验收测试。该测试满足了规范的要求,或者满足了系统的行为。之后,编写足够的生产/功能代码来完成验收测试。验收测试侧重于系统的总体行为。ATDD也被称为行为驱动开发(BDD)。
Developer TDD:开发人员TDD,您可以编写单个开发人员测试,即单元测试,然后只需编写足够的生产代码来完成该测试。单元测试侧重于系统的每一个小功能。开发者TDD被简单地称为TDD。
ATDD和TDD的主要目标是在及时(JIT)的基础上为解决方案指定详细的、可执行的需求。JIT意味着只考虑系统中需要的那些需求。所以提高效率。
使用 Agile Model Driven Development (AMDD)对TDD进行裁剪
TDD的优势在于详细说明和验证。它无法考虑更大规模的问题,如总体设计、系统使用或UI。AMDD解决了TDD没有解决的敏捷缩放(裁剪)问题。因此,AMDD用于解决更大规模的问题。
AMDD的生命周期
在模型驱动开发(MDD)中,在编写源代码之前创建广泛的模型。这又有一个灵活的方法?
在上面的图中,每个框代表一个开发活动。
设想是预测/设想将在项目第一周执行的测试的TDD过程之一。设想的主要目标是确定系统的范围和体系结构。高水平的需求和架构建模是为了成功的设想而做的。
它是一个过程,其中没有对软件/系统进行详细的规范,而是探索软件/系统的需求,从而定义项目的总体策略。
1. Iteration 0: 展望
这里有两项子活动:
Sub-task 1: 初始需求.
识别系统的高级需求和范围可能需要几天的时间。主要关注使用模型、初始域模型和用户界面模型(UI)。
Sub-task 2:初始架构
还需要几天时间来识别系统的体系结构。它允许为项目设置技术指导。主要关注于技术图、用户界面(UI)流、域模型和变更案例。
2. 迭代模型
这里团队必须为每个迭代计划将要完成的工作。
- 敏捷过程用于每个迭代,即在每个迭代期间,将优先添加新的工作项。
- 首先要考虑优先考虑的工作。添加的工作项可以在任何时候重新排序或从项堆栈中删除。
- 团队讨论如何实现每个需求。建模用于此目的。
- 建模分析和设计针对每个将要为该迭代实现的需求进行。
3. 模型风暴(Model storming)
这也被称为及时建模(Just in time Modeling)。
- 在此建模会议包括由2/3成员组成的小组,他们在纸上或白板上讨论问题。
- 一个团队成员会要求另一个和他们一起建模。这个建模过程大约需要5到10分钟。团队成员聚集在一起共享白板/纸张。
- 他们探索问题,直到找不到问题的主要原因。及时地,如果一个团队成员识别出他/她想要解决的问题,那么他/她将迅速得到其他团队成员的帮助。
- 其他小组成员然后探讨这个问题,然后大家继续像以前一样。它也被称为独立建模或客户QA会话。
4. 测试驱动开发(TDD)
- 它促进了应用程序代码和详细规范的确认性测试。
- 验收测试(详细需求)和开发人员测试(单元测试)都是TDD的输入。
- TDD使代码更简单明了。它允许开发人员维护更少的文档。
5. 评审
- 这是一个可选项,包括代码审查和模型评审(code inspections 和 model reviews)
- 这个活动可以在每次迭代结束时进行,也可以在整个项目结束时进行。
- 这个活动的好处是可以快速提供反馈。
TDD(Test Driven Development) vs. AMDD(Agile Model Driven Development)
TDD | AMDD |
|
|
|
|
|
|
|
|
|
|
|
|
TDD 的一个例子
一个对于用户密码验证的例子:
- 密码必须是5-10个字符
首先,我们编写代码实现上述需求:
Scenario 1:
为了运行测试,我们创建 PasswordValidator类。
执行测试,输出 PASSED
Scenario 2:
这里我们可以在方法TestPasswordLength()中看到,不需要创建PasswordValidator类的实例。实例意味着创建一个类的对象来引用该类的成员(变量/方法)。
我们将从代码中删除类PasswordValidator pv=new PasswordValidator()。我们可以通过PasswordValidator直接调用isValid()方法。IsValid(“Abc123”)。(见下图)
重构代码(Refactoring Code)
Scenario 3:
重构后的输出显示出失败状态(参见下面的图像),这是因为我们已经删除了实例。因此没有对非静态方法isValid()的引用。
因此,我们需要通过在布尔之前添加“.”单词作为公共静态布尔isValid(String password)来更改此方法。重构类PasswordValidator()以消除上述错误以通过测试。
在更改类PassValidator()之后,如果我们运行测试,那么输出将被PASSED,如下所示。
使用TDD的好处
- 尽早发现缺陷
开发人员测试他们的代码,但是在数据库领域,这通常包括手动测试或一次性脚本。使用TDD,随着时间的推移,您将构建一组自动化测试,您可以和任何其他开发人员随意重新运行这些测试。
- 更好的设计,更干净,更可扩展的代码
- 它有助于理解代码将如何使用以及它如何与其他模块交互。
- 其结果是更好的设计决策和更可维护的代码。
- TDD 允许编写具有单一职责的较小的代码,而不是具有多个职责的单块过程。这使得代码更容易理解。
- TDD 也强制只编写基于用户需求的测试代码来传递测试。
- 增强重构信心
- 如果对代码进行重构,则可能出现代码中断。因此,拥有一组自动化的测试,您可以在发布之前修复这些中断。如果使用自动测试时发现故障,将给出适当的警告。
- 使用TDD,应该可以产生更快、更可扩展的代码,并且具有更少的bug,这些bug可以以最小的风险进行更新。
- 对团队的好处
在团队成员缺席的情况下,其他团队成员可以轻松地获取并处理代码。TDD 还有助于知识共享,从而提高团队的整体效率。
- 对开发者的好处
尽管开发人员必须花费更多的时间来编写TDD测试用例,但是调试和开发新特性所花费的时间要少得多。开发者将编写更干净、更简单的代码。
总结:
- TDD 是一个调整Code的过程,目的是执行通过事先编写的Test
- TDD 更加强调创建生产代码而不是测试设计
- TDD 被称之为 测试先行(Test First Development)
- TDD 包括重构代码(Refactoring Code)
- 当使用TDD时,Code变得简单易懂