首页 | 软件中心 | Designand Inspiration
读库教程网 > 网络教程 > 技术开发 > C-C++ > 开放源码C/C++单元测试工具,第3部分:明白CppTest

开放源码C/C++单元测试工具,第3部分:明白CppTest

添加:2010年6月22日


  本文是探讨开放源码单元测试工具的系列的结尾一篇,细致引见 Niklas Lundell 开发的强悍框架 CppTest。CppTest 最大的优点是容易明白、掌握和运用。学习如何运用 CppTest 创立单元测试和测试套件、设计测试配备和定制回归测试日志格式,熟识 CppTest 提供的多个宏。关于高级用户,本文还提供 CppUnit 和 CppTest 框架之间的比拟。
  常用缩略词
  Html: 超文本标志言语
  I/O: 输进/输出
  XML: 可扩展标志言语
  装置和运用
  能够从 Sourceforge 不花钱下载 CppTest,采用的容许协议是 GNU Lesser General Public License (GPL)。按普通的开放源码配置-构建流程构建源代码。这会生成一个名为 libcpptest 的静态库。客户机代码必需包含下载的源代码中的头文件 cppTest.h,还要包含静态库 libcpptest.a 的链接。本文运用 CppTest version 1.1.0。
  什么是测试套件?
  单元测试用于测试源代码的特定局部。方式最容易的测试套件包含一组测试其他 C/C++ 代码的 C/C++ 函数。CppTest 在 Test 称号空间中定义一个名为 Suite 的类,它提供根本的测试套件功用。用户定义的测试套件必需扩展这个功用,定义作为实践单元测试运转的函数。清单 1 定义一个名为 myTest 的类,其中有两个函数,辨别测试一段源代码。TEST_ADD 是用于注册测试的宏。
清单 1. 扩展根本的 Test::Suite 类
#include “cppTest.h” 
 
class myTest : public Test::Suite { 
 void function1_to_test_some_code( ); 
 void function2_to_test_some_code( ); 
 
 myTest( ) { 
   TEST_ADD (myTest::function1_to_test_some_code); 
   TEST_ADD (myTest::function2_to_test_some_code); 
 } 
}; 


  扩展测试套件
  您能够很容易扩展测试套件的功用,创立测试套件的层次构造。每个测试套件能够测试不一样的代码局部,比如辨别测试编译器的分析、代码生成和代码优化,创立层次构造有助于更好地维护。清单 2 演示如何创立这样的层次构造。
清单 2. 创立测试套件层次构造
#include “cppTest.h” 
 
class unitTestSuite1 : public Test::Suite { … } 
class unitTestSuite2 : public Test::Suite { … } 
 
class myTest : public Test::Suite { 
 myTest( ) { 
   add (std::auto_ptr<Test::Suite>(new unitTestSuite1( ))); 
   add (std::auto_ptr<Test::Suite>(new unitTestSuite2( ))); 
 } 
}; 

  add 办法属于 Suite 类。清单 3 给出它的原型(取自头文件 cpptest-suite.h)。
清单 3. Suite::add 办法声明
class Suite 
{ 
 public: 
  … 
  void add(std::auto_ptr<Suite> suite); 
  ... 
} ; 

  运转第一个测试
  Suite 类的 run 办法担任执行测试。清单 4 演示如何运转测试。
清单 4. 以细致方式运转测试套件
#include “cppTest.h” 
 
class myTest : public Test::Suite { 
 void function1_to_test_some_code( ) { … }; 
 void function2_to_test_some_code( ) { … }; 
 
 myTest( ) { 
   TEST_ADD (myTest::function1_to_test_some_code); 
   TEST_ADD (myTest::function2_to_test_some_code); 
 } 
}; 
 
int main ( ) 
{ 
 myTest tests; 
 Test::TextOutput output(Test::TextOutput::Verbose); 
 return tests.run(output); 
} 


  run 办法前往一个 Boolean 值,只需一切单元测试都成功,这个值才会配置为 True。run 办法的参数是一个 TextOutput 类型的对象。TextOutput 类处置测试日志的输出。在默许情况下,日志输出到屏幕。
  除了细致方式,尚有简约方式。这两种方式的差异是,细致方式输出测试中失败的断言的行号/文件名信息,而简约方式只提供成功或失败的测试数目。
  失败后继续执行
  那么,假设一个测试失败了,会如何样?能不能继续执行测试由客户机代码决议。默许行为是继续执行其他测试。清单 5 给出 run 办法的原型。
清单 5. run 办法的原型
bool Test::Suite::run( 
  Output &  output, 
  bool     cont_after_fail = true  
); 

  假设在探测到第一个失败之后回归测试必需退出,那么 run 办法的第二个参数应该是 False。但是,能够不容易判别什么时分应该把第二个标志配置为 False。假定客户机代码试图向一个曾经完全满了的硬盘写信息:代码将失败,套件中行为类似的一切其他测试也会失败。在这种情况下,立刻中止回归测试是合理的。
  输出格式化器
  输出格式化器背后的思想是,能够须要不一样格式的回归测试运转报告:文本、HTML 等等。因而,run 办法本身并不转储后果,而是接受一个 Output 类型的对象,它担任显示后果。在 CppTest 中能够运用三种输出格式化器:
  Test::TextOutput。这是一切输出处置器中最容易的一种。显示方式能够是细致或简约。
  Test::CompilerOutput。依照与编译器构建日志类似的方式生成输出。
  Test::HtmlOutput。生成 HTML 输出。
  在默许情况下,这三种格式化器都把输动身送到 std::cout。前两种格式化器的构造器接受一个 std::ostream 类型的参数,它指定输出的目标,比如能够把输出转储到文件中以便进一步剖析。还能够创立输出格式化器的定制版本。为此,只需从 Test::Output 派生出用户定义的格式化器。能够议决清单 6 中的代码明白不一样的输出格式。

清单 6. 以简约方式运转 TEST_FAIL 宏
#include “cppTest.h” 
 
class failTest1 : public Test::Suite { 
void always_fail( ) { 
 TEST_FAIL (“This always fails!\n”); 
} 
public: 
 failTest1( ) { TEST_ADD(failTest1::always_fail); } 
}; 
 
int main ( ) { 
 failTest1 test1; 
 Test::TextOutput output(Test::TextOutput::Terse); 
 return test1.run(output) ? 1 : 0; 
} 

  留意,TEST_FAIL 是 cppTest.h 中预定义的宏,它会招致断言失败。(稍后进一步探讨。)清单 7 显示输出。
清单 7. 简约输出只显示失败数
failTest1: 1/1, 0% correct in 0.000000 seconds 
Total: 1 tests, 0% correct in 0.000000 seconds 

  清单 8 显示以细致方式运转类似代码时的输出。
清单 8. 细致输出显示文件/行信息、音讯、测试套件信息等等
failTest1: 1/1, 0% correct in 0.000000 seconds 
    Test:  always_fail 
    Suite:  failTest1 
    File:   /home/arpan/test/mytest.cpp 
    Line:  5 
    Message: "This always fails!\n" 
 
Total: 1 tests, 0% correct in 0.000000 seconds 

  运用编译器式格式的代码见清单 9。
清单 9. 用编译器式输出格式运转 TEST_FAIL 宏
#include “cppTest.h” 
 
class failTest1 : public Test::Suite { 
void always_fail( ) { 
 TEST_FAIL (“This always fails!\n”); 
} 
public: 
 failTest1( ) { TEST_ADD(failTest1::always_fail); } 
}; 
 
int main ( ) { 
 failTest1 test1; 
 Test::CompilerOutput output; 
 return test1.run(output) ? 1 : 0; 
} 


  留意,输出格式与 GNU Compiler Collection (GCC) 生成的编译日志类似,见清单 10。
清单 10. GCC 作风的编译器输出
/home/arpan/test/mytest.cpp:5: “This always fails!\n” 
  在默许情况下,编译器格式的输出是 GCC 作风的构建日志。但是,也可以够让输出采用 Microsoft® Visual C++® 和 Borland 编译器格式。清单 11 生成 Visual C++ 作风的日志并把日志转储到输出文件中。
清单 11. 用编译器式输出格式运转 TEST_FAIL 宏
#include <ostream> 
 
int main ( ) { 
 failTest1 test1; 
 std::ofstream ofile; 
 ofile.open("test.log"); 
 Test::CompilerOutput output( 
   Test::CompilerOutput::MSVC, ofile); 
 return test1.run(output) ? 1 : 0; 
} 

  清单 12 显示执行清单 11 中的代码之后 test.log 文件的内容。
清单 12. Virtual C++ 作风的编译器输出
/home/arpan/test/mytest.cpp (5) : “This always fails!\n” 
  结尾是最有意思的:运用 HtmlOutput 的代码。留意,HTML 格式化器并不在构造器中接受文件句柄,而是依托 generate 办法。generate 办法的第一个参数是一个 std::ostream 类型的对象,它的默许值是 std::cout(见源代码头文件 cpptest-htmloutput.h)。能够运用文件句柄把日志重定向到其他地点。清单 13 提供一个示例。
清单 13. HTML 作风的格式
#include *<ostream> 
 
int main ( ) { 
 failTest1 test1; 
 std::ofstream ofile; 
 ofile.open("test.log"); 
 Test::HtmlOutput output( ); 
 test1.run(output); 
 output.generate(ofile); 
 return 0; 
} 


  清单 14 显示在 test.log 中生成的局部 HTML 输出。
清单 14. 生成的 HTML 输出的片段
… 
<table summary="Test Failure" /> <tr> 
  <td style="width:15%" />  <td href="#/wangluoruanjian/liaotiangongju/3790.html" target="_blank" title="UC 下载" >UCcess">failTest1::always_fail</td> 
 </tr> 
 <tr> 
  <td style="width:15%" />  <td /> </tr> 
 <tr> 
  <td style="width:15%" />  <td /> </tr> 
</table> 
… 

  测试配备
  同一测试套件中的单元测试经常有类似的原始化需求:必需用某些参数创立对象、翻开文件句柄/操作系统端口等等。不须要在每个类办法中反复类似的代码,更好的办法是运用一些共用的原始化和退出例程,让每个测试都调用它们。必需在测试套件中定义配置和退出办法。清单 15 定义测试套件 myTestWithFixtures,它运用测试配备。
清单 15. 创立包含配备的测试套件
#include “cppTest.h” 
 
class myTestWithFixtures : public Test::Suite { 
 void function1_to_test_some_code( ); 
 void function2_to_test_some_code( ); 
 
 public: 
 myTest( ) { 
   TEST_ADD (function1_to_test_some_code); 
   TEST_ADD (function2_to_test_some_code); 
 } 
 
 protected: 
  virtual void setup( ) { … }; 
  virtual void tear_down( ) { … }; 
}; 


  留意,不须要显式地调用配置和退出办法。这些例程不必须要声明为虚拟的,除非方案现在扩展测试套件。这两个例程的前往类型必需是 void 并且不接受参数。
  CppTest 中的宏
  CppTest 提供多个有用的宏,能够在测试客户机源代码的实践办法中运用它们。这些宏在 cpptest-assert.h 中定义,cpptest.h 包含这个头文件。下面探讨一些宏和能够的用例。留意,除非另外声明,下面提供的输出都运用细致方式。
  TEST_FAIL (message)
  这个宏(见清单 16)无条件地发生失败。能够运用这个宏的典型情况是处置客户机函数的后果。假设后果不契合任何希冀的后果,那么抛出一个包含音讯的异常。当遇到 TEST_FAIL 宏时,不再执行单元测试中的其他代码。
清单 16. 运用 TEST_FAIL 宏的客户机代码
void myTestSuite::unitTest1 ( ) { 
  int result = usercode( ); 
  switch (result) { 
    case 0: // Do Something 
    case 1: // Do Something 
    … 
    default: TEST_FAIL (“Invalid result\n”); 
  } 
} 

  TEST_ASSERT (eXPression)
  这个宏与 C 断言库例程类似,但是 TEST_ASSERT 能够在调试和版本构建时起作用。假设表达式的后果是 False,就指出一个错误。清单 17 给出这个宏的内部完成。
清单 17. TEST_ASSERT 宏完成
#define TEST_ASSERT(expr) \ 
{  \ 
 if (!(expr)) \ 
 { \ 
 assertment(::Test::Source(__FILE__, __LINE__, #expr)); \ 
   if (!continue_after_failure()) return; \ 
 } \ 
} 


  TEST_ASSERT_MSG (expression, message)
  这个宏与 TEST_ASSERT 类似,只是在断言失败时在输出中显示音讯而不是表达式。下面是包含和不包含音讯的宏示例。
TEST_ASSERT (1 + 1 == 0); 
TEST_ASSERT (1 + 1 == 0, “Invalid expression”); 

  清单 18 显示遇到这两个宏时的输出。
清单 18. TEST_ASSERT 和 TEST_ASSERT_MSG 宏的输出
Test:  compare 
Suite:  CompareTestSuite 
File:   /home/arpan/test/mytest.cpp 
Line:  91 
Message: 1 + 1 == 0 
 
Test:  compare 
Suite:  CompareTestSuite 
File:   /home/arpan/test/mytest.cpp 
Line:  92 
Message: Invalid Expression 

  TEST_ASSERT_DELTA (expression1, expression2, delta)
  假设 expression1 和 expression2 的差超越了 delta,就抛出异常。这个宏在 expression1 和 expression2 是浮点数的情况下尤其有用;比如,依据实践采用的舍入办法不一样,4.3 能够存储为 4.299999 或 4.300001,因而在举行比拟时须要 delta。另一个示例是测试操作系统 I/O 的代码:翻开文件破费的时间不能够每次都类似,但是必需在必须的范围内。
  TEST_ASSERT_DELTA_MSG (expression, message)
  这个宏与 TEST_ASSERT_DELTA 宏类似,只是在断言失败时还显示音讯。
  TEST_THROWS (expression, exception)
  这个宏检验表达式并希冀遇到某种异常。假设没有捕捉到异常,就触发断言。留意,对表达式的实践值并不举行任何测试;要测试的是异常。请思索清单 19 中的代码。

清单 19. 处置整数异常
class myTest1 : public Test::Suite { 
 … 
 void func1( ) { 
   TEST_THROWS (userCode( ), int); 
 } 
 public: 
   myTest1( ) { TEST_ADD(myTest1::func1); } 
}; 
 
void userCode( ) throws(int) { 
  … 
  throw int; 
} 

  留意,userCode 例程的前往类型并不首要:它也可以够是双精度数或整数。由于这里的 userCode 无条件地抛出 int 类型的异常,所以这个测试会顺利议决。
  TEST_THROWS_ANYTHING (expression)
  有时分,客户机例程依据详细情况抛出不一样类型的异常。能够运用 TEST_THROWS_ANYTHING 宏处置这种情况,这个宏不须要指定希冀的异常类型。只需在执行客户机代码之后捕捉到异常,就不会触发断言。
  TEST_THROWS_MSG (expression, exception, message)
  这个宏与 TEST_THROWS 类似,只是它输出音讯而不是表达式。请思索以下代码:
TEST_THROWS(userCode( ), int); 
TEST_THROWS(userCode( ), int, “No expected exception of type int”); 

  清单 20 显示这些断言失败时的输出。
清单 20. TEST_THROWS 和 TEST_THROWS_MSG 宏的输出
Test:  func1 
Suite:  myTest1 
File:  /home/arpan/test/mytest.cpp 
Line:  24 
Message: userCode() 
 
Test:  func2 
Suite:  myTest1 
File:  /home/arpan/test/mytest.cpp 
Line:  32 
Message: No expected exception of type int 


  CppUnit 和 CppTest 的比拟
  本系列的 第 2 局部 探讨了另一个盛行的开放源码单元测试框架 CppUnit。CppTest 框架比 CppUnit 容易得多,但是成效也不错。下面扼要地比拟这两个强悍的工具:
  创立单元测试和测试套件的简便性。CppUnit 和 CppTest 都创立类办法方式的单元测试,类本身都派生自工具提供的 Test 类。但是,CppTest 的语法略微容易些,测试的注册在类构造器内举行。关于 CppUnit,须要额外的宏 CPPUNIT_TEST_SUITE 和 CPPUNIT_TEST_SUITE_ENDS。
  运转测试。CppTest 直接调用测试套件上的 run 办法,而 CppUnit 运用单独的 TestRunner 类,议决调用这个类的 run 办法运转测试。
  扩展测试层次构造。关于 CppTest,随时能够从以前的测试套件派生新类,从而扩展测试套件。新的类定义一些新函数,这些函数成为新的单元测试。只需在新类类型的对象上调用 run 办法。与之相反,CppUnit 须要运用 CPPUNIT_TEST_SUB_SUITE 宏和类承袭来完成类似的成效。
  生成格式化的输出。CppTest 和 CppUnit 都准许定制输出。但是,CppTest 有预定义的 HTML 输出格式化器,而 CppUnit 没有。但是,只需 CppUnit 支持 XML 格式化。它们都支持文本和编译器式格式。
  创立测试配备。要想运用测试配备,CppUnit 要求测试类派生自 CppUnit::TestFixture。您必需提供配置和退出例程的定义。关于 CppTest,只需提供配置和退出例程的定义。这个处置方案显然更好,由于这让客户机代码更容易。
  预定义的宏支持。CppTest 和 CppUnit 都提供一组用于断言、处置浮点等的宏。
  头文件。 CppTest 只需求包含一个头文件,而 CppUnit 客户机代码必需依据运用的特征包含多个头文件,比如 HelperMacros.h 和 TextTestRunner.h。
  完毕语
  单元测试是当今软件开发流程的根本局部,CppTest 能够协助 C/C++ 开发人员执行代码测试,从而降低维护难度。应该留意,本系列中探讨的三个工具(Boost 测试框架、CppUnit 和 CppTest)运用类似的根本观点,比如配备、用于断言的宏和输出格式化器。这些工具都是开放源码的,能够依据须要悄然松松地定制代码(比如 CppTest 的 XML 输出器)。您还等什么呢?开端试试吧!

读库教程网文章由网络收集后整理发布,文章发布人拥有该内容的所有权力及责任!

如果你喜欢这页,可以按Ctrl+D收藏起来。

相关内容
上一个内容:C#中海量数据的批量插入和更新
下一个内容:没有了
相关评论
公益广告
精彩推荐
友情链接: 百分百青年 | 烛光信息网 | 夏布新网 | 新育互联网
管理员:QQ:27038219, E-mail:27038219@qq.com今日更新
读库教程网所有文章从网络收集所发布,文章发布人拥有该内容的所有权力及责任,转载时请注明出处!
Template designed by www.dkuu.com. Optimized for 1024x768 to Firefox,Opera and MS-IE6/IE7.