如何在Visual C++ 2010中实践测试驱动开发

发表于:2011-02-23来源:作者:点击数: 标签:软件开发开发者目标极限传统
一个高效的软件 开发 过程对软件开发人员来说是至关重要的,决定着开发是痛苦的挣扎,还是不断进步的喜悦。国人对软件蓝领的不屑,对繁琐冗长的传统开发过程的不耐,使大多数开发人员无所适从,而测试驱动开发(Test-Driven Development)就是为了改善传统的以

  一个高效的软件开发过程对软件开发人员来说是至关重要的,决定着开发是痛苦的挣扎,还是不断进步的喜悦。国人对软件蓝领的不屑,对繁琐冗长的传统开发过程的不耐,使大多数开发人员无所适从,而测试驱动开发(Test-Driven Development)就是为了改善传统的以实现为目标的软件开发流程,利用测试来驱动软件程序的设计和实现,从测试的角度提出的一种全新的开发方式。测试驱动开发可以有效的避免过度设计带来的浪费,同时也可以让开发者在开发中拥有更全面的视角,避免过度实现带来的浪费。因此,测试驱动开发成为极限编程中比较流行的一种开发方式,受到很多开发者的青睐。

  测试驱动开发的整个过程跟传统的软件开发过程有很大的区别,它的基本过程如下:

  1) 明确当前要完成的功能。可以记录成一个 TODO 列表。

  2) 快速完成针对此功能的测试用例编写。

  3) 测试代码编译不通过。

  4) 编写对应的功能代码。

  5) 测试通过。

  6) 对代码进行重构,并保证测试通过。

  7) 循环完成所有功能的开发。

  为了保证这一过程能够快捷方便地进行,通常我们会采用很多开发工具来支持这一过程。在应用最为广泛的开发工具Visual Studio中,因为有.NET Framework的支持,我们可以很轻松方便地进行C#和Visual Basic语言的测试,所以使用这两种语言实践测试驱动开发也很方便。但是,作为Visual Studio中最重要的开发语言的C++,在以往的Visual Studio的版本中也没法方便地进行测试。如果要实践面向C++语言的测试驱动开发,我们不得不借助第三方测试工具,比如CPPUnit来帮助进行测试。在整个过程中,我们要使用CPPUnit进行测试,而开发又是在Visual Studio中进行,两个工具的衔接协作,给测试驱动开发带来了很多不便。使用测试驱动开发流程的开发人员热切地盼望有一个面向C++的开发工具可以把测试驱动开发过程中最重要的两个过程:“测试”和“开发”结合起来,两者能够做到无缝衔接,让测试真正地驱动开发。

  幸运的是,开发人员的这一梦想在Visual Studio 2010中成为了现实。全新的Visual Studio 2010已经不仅仅是一个开发工具,它也集成了大量的测试工具,成为一个完整的开发平台。在Visual Studio 2010中,我们可以创建面向C++的测试项目来完成测试驱动开发流程中的测试环节,从而让整个测试驱动开发过程都在Visual Studio中进行,让测试和开发做到了无缝衔接,而测试也真正地驱动了开发。

  光说不练假把式。为了让大家更加了解如何在Visual Studio中进行面向C++的测试驱动开发,我们来看一个实际的例子,在例子中体会这一过程是多么简单方便。例如,我们要编写一个计算工资的类Salary,它可以根据员工的入职年份和现在的年份计算整个员工应该得到的工资。

  按照测试驱动开发的过程,我们首先设计完成这个Salary类需要实现的功能,为了简便,我们让这个类只需要完成两个简单的功能:

  1) 能够给定员工的入职年份,并根据现在的年份给出应得的工资

  2) 能够对错误的输入年份返回相应的错误代码

  既然是测试驱动开发,当然是“开发未动,测试先行”了。按照下面的步骤,首先创建一个测试项目并编写测试对设计中的功能进行测试:

  1) 启动Visual Studio 2010并创建一个新的“Visual Studio空白解决方案”,方案名字叫做SalarySys。接下来的所有测试和开发都会在这个解决方案中进行。

  2) 向刚刚创建的解决方案中添加一个Visual C++测试项目SalaryTest。因为我们的测试需要使用C++/CLI进行编写以便使用.NET的单元测试框架,所以我们同时要修改测试项目属性,让它使用公共语言运行时(/clr)支持。

  3) 向测试项目SalaryTest中添加一个单元测试。默认情况下,Visual Studio会为我们创建一个UnitTest1.cpp文件,在其中我们就可以编写针对将要实现的工资计算类测试了。

  4) 在UnitTest1.cpp文件中找到“#pragma region Additional test attributes”,在这个区域中,我们编写一个测试来对Salary的基本功能进行测试。

  // 创建测试类的智能指针

  // 测试功能设计中的“能够给定员工的入职年份”

  std::unique_ptr pClass(new Salary(2003));

  // 测试功能设计中的“根据现在的年份给出应得的工资”

  // 判断函数返回结果是否符合预期

  Assert::AreEqual(1900, pClass->GetSalary(2006));

  这里我们首先创建了一个Salary类的实例智能指针,其中构造函数的参数2003表示入职年份,然后调用其GetSalary()函数计算工资,其参数2006表示现在的年份。按照设计的计算规则,其结果应该是1300,这里我们使用Assert::AreEqual函数对测试结果进行判断,如果这个断言函数通过,则表示这个Salary类的测试通过。

  除了使用Assert::AreEqual断言函数对结果进行判断之外,Visual C++还提供了多种断言函数,以满足对不同类型的返回结果进行判断的需要。更人性化的是,我们还可以在断言函数中添加对测试结果的说明,这样我们更容易以测试的结果来驱动开发。例如:

  // 判断不相等

  Assert::AreNotEqual(0, (DWORD_PTR) pClass, "pClass指针不应该为空指针");

  // 判断相等

  Assert::AreEqual(0, (DWORD_PTR) pClass, "pClass指针应该为空指针");

  // 判断比较结果是否为true

  Assert::IsTrue(pClass == nullptr, "pClass指针应该为空指针");

  // 判断StringValue()返回的字符串是否与期望的结果相等

  Assert::AreEqual("期望的结果", gcnew String(pClass->StringValue());

原文转自:http://www.ltesting.net