在开发可测试软件的过程中,单元测试已成为确保软件质量的一个不可或缺部分。测试驱动开发(Test-Driven Development,TDD)是编写单元测试的一种方法,采用该方法的开发人员在编写任何产品代码之前都需要编写测试程序。TDD 允许开发人员以系统的方式完善软件设计,从而有效的提高单元测试的质量,增加回归测试(指修改代码后的再次测试)带来的好处。

 

单元测试和测试驱动开发的意义

       当谈到软件测试时,通常是指进行的一系列不同种类的测试,包括单元测试验收测试(acceptance testing)、探索测试(exploratory testing)、性能测试(performance testing)、可扩展性测试(scalability testing)等。

 

       大部分的单元测试通常具有以下 4 个特点:

  • 测试小部分产品代码(单元)

       编写单元测试时,我们经常查找能够合理测试的最小功能片段。在像 C# 这样的面向对象编程语言中,类通常就意味着是最小的功能片段,但大多数情况下,我们测试的是类中的一个方法。源代码的阅读次数远超过编写次数,这点在单元测试中特别有用,因为单元测试要测试软件的期望规则和行为。当单元测试失败时,开发人员应该能够快速的阅读测试程序,理解什么出错了,为什么会出错,从而能够快速的修正出错的地方。使用小的测试程序来测试小片段代码能够极大的改善测试结果的可理解性。      

  • 产品代码分块隔离测试

       单元测试另一个重要方面是它能够在问题出现时精确的指出问题出现的位置。把测试的代码与和它有交互的复杂代码隔离,以确保出现的故障一定是在测试代码中,而不是在与其交互的代码中(检查交互的合作代码是否存在 bug 是合作代码单元测试的任务)。

  • 只测试公共端点

       在修改类的内部实现时,有时,对代码的一点点修改就可能会导致多个单元测试的失败。因此,在修改产品代码时,维护这些单元测试的人员通常会感到很沮丧。之所以会这样,是因为单元测试对它要测试的类的工作原理了解太多。当编写单元测试时,如果仅局限于产品的公共端(一个组件的集成点),就可以将单元测试与组件的许多内部实现细节相隔离,这样,修改实现细节就不会经常破坏已经编写好的单元测试了

  • 运行测试程序能够得到自动的结果:pass / fail

       如果对每一小段代码编写测试程序,显而易见,最终我们会编写很多单元测试。如果测试过程不是自动的,这将会损耗开发人员的大部分精力。为了获得自动过程,开发人员通常使用单元测试框架。框架允许开发人员使用自己擅长的编程语言和开发环境编写测试程序,然后创建 pass / fail 规则集,之后框架会判定测试是否成功。单元测试框架中通常有一个称为运行程序(runner)的小软件,可用来在项目中查找和执行单元测试。有很多这样的软件,一些集成到了 VS 中,一些集成到了 GUI 中,一些需要命令行运行等。

 

 

测试驱动开发的定义(TDD)

       测试驱动开发指的是利用单元测试来驱动产品代码设计的过程。首先编写单元测试,然后编写产品代码使其通过测试。当把单元测试作为质量保障机制时,主要指的是减少软件中的漏洞。TDD 可以实现这一目标,但这不是它的主要目标;TDD 的主要目标是提高软件设计的质量。通过首先编写单元测试,我们可以在编写任何产品代码之前描述想要组件执行的操作。由于还没有产品代码的详细实现,因此,我们不会把精力放到产品代码的任何具体实现上,单元测试变成了产品代码的消费者。

  • 红 / 绿 周期

       仍然遵循前面为单元测试设置的指导原则:编写小段代码、隔离测试和自动执行测试。由于首先编写测试程序,因此当使用 TDD 时,经常会进入一个周期步骤

  1. 编写一个单元测试
  2. 运行单元测试,得到 fail 结果(因为尚未编写产品代码)
  3. 编写足够的产品代码,通过但愿测试
  4. 运行单元测试程序,得到 pass 结果

       重复以上步骤,直到产品代码编写完毕为止。由于大部分的单元测试框架用红色的 文本 / UI 元素表示失败,用绿色表示通过,因此,这个周期又成为 红 / 绿 周期

 

  • 重构

       “重构”一词具有多种意义,这里是指在不改变产品代码外部可见功能的情况下,修改产品代码实现细节的过程。在重构和更新产品代码的过程中,单元测试应该能够继续通过。重构时不需要修改任何单元测试程序;如果要求必须修改单元测试,则要按照“红 / 绿 周期”的步骤来添加、删除或改变,切勿同时修改测试程序和产品代码。更确切的说,重构是一种机制,在不破坏单元测试程序的情况下,构建结构化代码的过程

 

  • 采用 Arrange(准备测试环境)、Act(在测试中调用的方法)、Assert(确保按预期执行) 结构化测试

       本文中许多例子都遵照一个成为“Arrange、Act、Assert ”的结构(3A),单元测试的代码如下所示:

[TestMethod]
public void PoppingReturnsLastPushedItemFromStack()
{
    // Arrange
    Stack<string> stack = new Stack<string>();
    string value = "Hello World!";
    stack.Push(value);
 
    // Act
    string result = stack.Pop();
 
    // Assert
    Assert.AreEqual(value, result);        
}

相关文章: