﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>C++博客-不倦的候鸟成长日记-随笔分类-C++单元测试框架</title><link>http://www.cppblog.com/shuiyuan2004/category/4978.html</link><description>——候鸟，候补的菜鸟也</description><language>zh-cn</language><lastBuildDate>Tue, 20 May 2008 18:38:56 GMT</lastBuildDate><pubDate>Tue, 20 May 2008 18:38:56 GMT</pubDate><ttl>60</ttl><item><title>[转]Boost Test Library</title><link>http://www.cppblog.com/shuiyuan2004/archive/2008/03/29/45677.html</link><dc:creator>不倦</dc:creator><author>不倦</author><pubDate>Sat, 29 Mar 2008 07:39:00 GMT</pubDate><guid>http://www.cppblog.com/shuiyuan2004/archive/2008/03/29/45677.html</guid><wfw:comment>http://www.cppblog.com/shuiyuan2004/comments/45677.html</wfw:comment><comments>http://www.cppblog.com/shuiyuan2004/archive/2008/03/29/45677.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/shuiyuan2004/comments/commentRss/45677.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/shuiyuan2004/services/trackbacks/45677.html</trackback:ping><description><![CDATA[<strong>boost 的 Test<br><br></strong>test 库中有如下的组件：<br><strong>Execution Monitor</strong>&nbsp; 一个基本用于 program 和 test program 的异常与错误检测与报告机制，Execution Monitor 调用用户提供的函数并报告所有捕获的运行时的异常，它只被其他 Boost Test Library components 内部调用，当然也可以用于一些 production environment 控制那些会导致程序崩溃的函数的调用；<br><br><strong>Program Execution Monitor</strong>&nbsp; 一个简单的 helper facility 用于监控一个程序的运行，Program Execution Monitor 提供了 main() 函数和 Execution Monitor 监控程序的执行，可以用以 production environment 产生一致错误报告，控制在 test environment 环境中运行的程序，直接使用 Test Execution Monitor；<br><br><strong>Test Tools</strong>&nbsp; 一个用以进行 testing 的一个 toolbox，Test Tools 被用来测试在 Test Execution Monitor 或 Unit Test Framework 控制下运行的程序；<br><br><strong>Test Execution Monitor</strong>&nbsp; 让一个测试程序在 monitored environment 环境中运行，Test Execution Monitor 提供了 main() 来控制被测试程序的运行并可以让 Test Tools 实现测试的逻辑，它被用在 test environment，如果要控制 production code 的运行使用 Program Execution Monitor；<br><br><strong>Unit Test Framework</strong>&nbsp; 用以简化编写和组织 test cases 的 framework，支持简单函数或者是成员函数编写的 test cases 并将他们组织成一个 test suites 的 tree，该 framework 使用 Test Tools 来实现 test cases 并提供了一种机制用以管理 log report level 和 result report level；<br><br><strong>minimal testing facility</strong>&nbsp; 提供 Boost Test 最初版本的提供的功能的简单 facility，提供了和 Test Execution Monitor 一样的机制，但额外定义了一些简单提供 Test Tools 类似功能的 test tools，它不需要和任何的外部组件 link，适合简单和快速测试的需要，使用于 test environment。<br>(http://yaekees.spaces.live.com/blog/cns!1955EE8C6707277A!146.entry)<br><br><br><strong>boost Test 的 Execution Monitor</strong><br><br>使用 Execution Monitor 的三部曲：<br>1. #include &lt;boost/test/execution_monitor.hpp&gt;<br>2. Make an instance of boost::execution_monitor<br>3. Optionally register custom exception translators for exception classes you want special processing<br>&nbsp;<br>调用 execution_monitor::execute( function_to_monitor, catch_system_exception, timeout ) 运行 monitored function。如果调用成功则返回一个 integer value，如果有如下的事情发生<br>1. Uncaught C++ exception<br>2. hardware or software signal, trap, or other exception<br>3. Timeout reached<br>4. debug assert event occurred (under Microsoft Visual C++ or compatible compiler)<br>the method execution_monitor::execute( ... ) throws the boost::execution_exception<br>如果希望程序 error message 被转化为&nbsp; execution_exception 的 error message，则扔出如下的三类异常：C string，std:string，any exception class in std::exception hierarchy。<br>&nbsp;<br>终止 monitored function 而不让 Execution Monitor 报告 any error 的最佳方法是抛出 boost::execution_aborted。如果不喜欢 "unknown exception caught" message 而更愿意使用自定义的 exception，可以向 execution monitor 为 any exception types 注册 translator 函数，如<br>ex_mon.register_exception_translator&lt;my_exception1&gt;( &amp;translate_my_exception1 );<br>my_exception1 是异常类型，translate_my_exception1 是异常处理函数。<br>class execution_monitor {<br>public:<br>&nbsp;&nbsp;&nbsp; virtual&nbsp;&nbsp;&nbsp;&nbsp; ~execution_monitor();<br>&nbsp;&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp; template&lt;typename Exception, typename ExceptionTranslator&gt;<br>&nbsp;&nbsp;&nbsp; void&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; register_exception_translator( ExceptionTranslator const&amp; tr, boost::type&lt;Exception&gt;* = 0 );<br>&nbsp;<br>&nbsp;&nbsp;&nbsp; int&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; execute( unit_test::callback0&lt;int&gt; const&amp; F, bool catch_system_errors = true, int timeout = 0 );<br>}; // exception monitor<br>&nbsp;<br>Execution Monitor 用 boost::execution_exception 报告捕获的问题，其最大的特点是不分配任何 memory 因此在内存稀缺环境中使用。<br>class execution_exception {<br>public:<br>&nbsp;&nbsp;&nbsp; execution_exception( error_code ec, const_string what_msg );<br>&nbsp;&nbsp;&nbsp; enum error_code {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; cpp_exception_error,&nbsp;&nbsp;&nbsp; // see note (1) below<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; user_error,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // user reported nonfatal error<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; system_error,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // see note (2) below<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; timeout_error,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // only detectable on certain platforms<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; user_fatal_error,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // user reported fatal error<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; system_fatal_error&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // see note (2) below<br>&nbsp;&nbsp;&nbsp; };<br>&nbsp;&nbsp;&nbsp; error_code&nbsp;&nbsp; code() const;&nbsp; // use this method to get an error code for the exception<br>&nbsp;&nbsp;&nbsp; const_string what() const;&nbsp; // use this method to get an error message for the exception<br>};<br>Note 1 ：uncaught C++ exceptions 被当做 error，如果应用程序捕获到 C++ exception，则该 exception 不会到 boost::execution_monitor；<br>Note 2 ：这些 error 包括 UNIX signals 和 Windows structured exceptions，这些经常由 hardware traps 触发。<br><br>execution_monitor 可以动态连接，库中没有提供 main 函数。由于只有一个 libs/test/execution_monitor.cpp 文件，因此可以直接 copy 该文件。<br>(http://yaekees.spaces.live.com/blog/cns!1955EE8C6707277A!148.entry)<br><br><br><strong>boost Test 的 Program Execution Monitor<br><br></strong>C++ program 可以通过 return value 和 throwing an exception 报告 user-detected errors，而如 dereferencing an invalid pointer 的 System-detected errors 则以其他方式报告。<br>&nbsp;<br>Boost Test Library 的 Program Execution Monitor 减轻了用户处理复杂的 error detection 和 reporting，并提供了 main() 函数在 monitored environment 来调用用户提供的 cpp_main() 函数，main() 函数以一致的方式检测和报告多种 errors 的发生，将其转化为一致的 return code 返回给 host enviroment。<br>BOOST_TEST_CATCH_SYSTEM_ERRORS 设置允许 Execution Monitor 捕获 system errors，默认值为 "yes"。<br>BOOST_PRG_MON_CONFIRM 设置是否允许用户交互确认程序正确运行，默认值为"yes"。<br>&nbsp;<br>Program Execution Monitor 使用 Execution Monitor 监视用户提供的 cpp_main() 函数的运行。尽管 Program Execution Monitor 在 libs/test/src/cpp_main.cpp 提供了 main() 函数，但是为了 link 正确，用户必须提供一个与 main() 函数一样接口的 cpp_main() 函数。如 cpp_main() 抛出异常或返回非 0 值，Program Execution Monitor 就认为程序发生错误。<br>Program Execution Monitor 在 cout 和 cerr 流上分别提供详细和简要的错误报告。Program Execution Monitor 提供的 main() 函数返回如下的值<br>1. boost::exit_success - no errors<br>2. boost::exit_failure - non-zero and non-boost::exit_success return code from cpp_main().<br>3. boost::exit_exception_failure - cpp_main() throw an exception.<br>(http://yaekees.spaces.live.com/blog/cns!1955EE8C6707277A!149.entry)<br><br><br><strong>boost Test 的 Test Tools<br><br></strong>Test Tools 为了让用户使用方便，提供了一系列的宏，这些宏分三级，其效果是不同的：<br>WARN 不增加引用计数，继续执行程序<br>CHECK 增加应用计数，继续执行程序<br>REQUIRE 增加应用计数，中断程序的运行<br>&nbsp;<br>使用 CHECK level tools 实现 assertions，使用 WARN level tools 检验不太重要但是正确的方面，如：性能、可移植性、有用性，如 assertions 失败就不应该让程序继续运行则使用 REQUIRE level tools。<br>&nbsp;<br>Test Tools 提供了两个 .hpp 文件 boost/test/test_tools.hpp 和 boost/test/floating_point_comparison.hpp，一个 .cpp 文件 libs/test/test_tools.cpp<br>BOOST_WARN( P )<br>BOOST_CHECK( P )<br>BOOST_REQUIRE( P )<br>BOOST_WARN_MESSAGE( P, M )<br>BOOST_CHECK_MESSAGE( P, M )<br>BOOST_REQUIRE_MESSAGE( P, M )<br>BOOST_ERROR( M )BOOST_FAIL( M )<br>BOOST_MESSAGE( M )<br>BOOST_CHECKPOINT( M )<br>BOOST_WARN_THROW( S, E )<br>BOOST_CHECK_THROW( S, E )<br>BOOST_REQUIRE_THROW( S, E )<br>BOOST_WARN_EXCEPTION( S, E, P )<br>BOOST_CHECK_EXCEPTION( S, E, P )<br>BOOST_REQUIRE_EXCEPTION( S, E, P )<br>BOOST_IGNORE_CHECK( e )<br>BOOST_WARN_NO_THROW( S )<br>BOOST_CHECK_NO_THROW( S )<br>BOOST_REQUIRE_NO_THROW( S )<br>BOOST_WARN_CLOSE( L, R, T )<br>BOOST_CHECK_CLOSE( L, R, T )<br>BOOST_REQUIRE_CLOSE( L, R, T )<br>BOOST_WARN_SMALL( FPV, T )<br>BOOST_CHECK_SMALL( FPV, T )<br>BOOST_REQUIRE_SMALL( FPV, T )<br>BOOST_WARN_PREDICATE( P, ARGS )<br>BOOST_CHECK_PREDICATE( P, ARGS )<br>BOOST_REQUIRE_PREDICATE( P, ARGS )<br>BOOST_WARN_EQUAL_COLLECTIONS( L_begin, L_end, R_begin, R_end )<br>BOOST_CHECK_EQUAL_COLLECTIONS( L_begin, L_end, R_begin, R_end )<br>BOOST_REQUIRE_EQUAL_COLLECTIONS( L_begin, L_end, R_begin, R_end )<br>BOOST_WARN_BITWISE_EQUAL( L, R )<br>BOOST_CHECK_BITWISE_EQUAL( L, R )<br>BOOST_REQUIRE_BITWISE_EQUAL( L, R )<br>BOOST_IS_DEFINED( symb )<br>BOOST_BITWISE_EQUAL( L, R )<br>(http://yaekees.spaces.live.com/blog/cns!1955EE8C6707277A!145.entry)<br><br><br><strong>boost Test 的 Test Execution Monitor<br><br></strong>Test Execution Monitor 结合了 Test Tools 和 Execution Monitor 的特点简化了烦琐测试工作，它提供了 main() 函数调用用户提供的 test_main() 函数，用户可以使用 Test Tools 来进行复杂的验证工作。<br>&nbsp;<br>Test Execution Monitor 被设计用来进行测试简单的程序或者从已存在的 production code 中 dig a problem。Program Execution Monitor 更适合监控 production (non-test) programs（因为它不影响程序的性能），而 Unit Test Framework 更适合 complex test programs，因为 Unit Test Framework 可以<br>1. 可以将 test 分割到多个 test cases，它会为每一个 test case 分别产生 pass/fail 的统计信息；<br>2. 假如某一个 test case 失败了不会影响其他的 test；<br>3. 可以通过指定 name 来运行特定的 test case<br>4. 分离的 test cases 运行更清楚发现特定测试模块的目的<br>5. 可以设置更多的选项<br>(http://yaekees.spaces.live.com/blog/cns!1955ee8c6707277a!150.entry)<br><br><br><strong>boost Test 的 Unit Test Framework<br><br></strong>regression testing 只关注程序运行的时候是否有错误发生，而 unit test 则需要尽可能详细的输出错误的信息。Unit Test Framework 用 test tools 简化了 test cases 的编写并将其组织为有层次的 test suites。它提供了 main() 函数初始化 framework，通过命令行参数或者是环境变量设置参数，并通过 init_unit_test_suite(argc, argv)，然后运行用户的 test suite。Framework 跟踪所有的 passed/failed 的 Test Tools assertions，可以通过 test cases 的数目（part 和 total）得到测试进度，并且可以多种形式提供结果。Unit Test Framework 可被用来进行简单测试和复杂重要测试，它不适合用在 production code，同时它以运行时的效率为代价加速编译。<br>&nbsp;<br>该函数负责创建和初始化顶层的 test_suite 实例，如 test_suite 创建失败该函数返回 NULL 指针，测试终止并返回一个 boost::exit_test_failure。Framework 在测试运行时传递特定的命令行参数，同时排斥其他 framework 指定的参数，同时 framework 负责 test_suite 的生命周期，在 test 终止时会被销毁。<br>&nbsp;<br>假如 test cases 会丢出自定义的异常，可以像 Execution Monitor 那样注册特定的 translator，注册函数的原型定义如下<br>template&lt;typename Exception, typename ExceptionTranslator&gt;<br>void boost::unit_test::register_exception_translator( ExceptionTranslator const&amp; tr, boost::type&lt;Exception&gt;* d = 0 ) <br>&nbsp;<br>一旦测试结束，framework 会报告结果并返回 return code。下面是集成在 unit test framework 内部的的返回值<br>boost::exit_success&nbsp; returned if no errors occurred during test or success result code was explicitly requested with the no result code framework parameter<br>boost::exit_test_failure&nbsp; returned if nonfatal errors detected and no uncaught exceptions thrown or the framework fails to initialize the test suite<br>boost::exit_exception_failure&nbsp; returned if fatal errors detected or uncaught exceptions thrown<br>&nbsp;<br>在 VC7.1+stlport 4.62 上 unit_test_example3.cpp 没有通过(对多种继承没有通过)<br>&nbsp;<br>简单的使用方法：<br>1. 首先定义 #define BOOST_AUTO_TEST_MAIN<br>2. 包含 #include &lt;boost/test/auto_unit_test.hpp&gt;<br>3. 创建一个 test_suite<br>BOOST_AUTO_TEST_CASE( test )<br>{<br>&nbsp;&nbsp;&nbsp; BOOST_CHECK( true );<br>}<br>然后 link libboost_test_exec_monitor-vc71-mt-sp-1_33.lib 就可以了。<br>&nbsp;<br>另一类使用方法，首先包含如下<br>#include &lt;boost/test/unit_test.hpp&gt;<br>#include &lt;boost/test/unit_test_monitor.hpp&gt;<br>using namespace boost::unit_test;<br>然后声明 test_suite* init_unit_test_suite(int /*argc*/, char* /*argv*/[]) {}<br>一个典型用法是<br>test_suite* init_unit_test_suite( int /*argc*/, char* /*argv*/[] ) {<br>&nbsp;&nbsp;&nbsp; test_suite* test = BOOST_TEST_SUITE("custom_exception_test");<br>&nbsp;&nbsp;&nbsp; unit_test_monitor.register_exception_translator&lt;my_exception1&gt;( &amp;my_exception1_translator );<br>&nbsp;&nbsp;&nbsp; unit_test_monitor.register_exception_translator&lt;my_exception2&gt;( &amp;my_exception2_translator );<br>&nbsp;&nbsp;&nbsp; test-&gt;add( BOOST_TEST_CASE( &amp;throw_my_exception1 ) );<br>&nbsp;&nbsp;&nbsp; test-&gt;add( BOOST_TEST_CASE( &amp;throw_my_exception2 ) );<br>&nbsp;&nbsp;&nbsp; return test;<br>}<br>(http://yaekees.spaces.live.com/blog/cns!1955EE8C6707277A!151.entry)<br><br><br><strong>boost 的 minimal testing facility<br><br></strong>只适合使用在 test environment 中，不需要 link 任何外部的组件。用户只需要提供了如下的函数<br>int test_main( int argc, char* argv[] ) 就可以了。minimal testing facility 提供了 BOOST_CHECK(predicate)，BOOST_REQUIRE(predicate)，BOOST_ERROR(message)， BOOST_FAIL(message)。除了这四个 MACRO 以外可以通过抛出异常和 return 返回值报告错误。下面是 boost 提供的一个示例。<br>#include &lt;boost/test/minimal.hpp&gt;<br>int add( int i, int j ) { return i+j; }<br>int test_main( int, char *[] )&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // note the name!<br>{<br>&nbsp;&nbsp;&nbsp; // six ways to detect and report the same error:<br>&nbsp;&nbsp;&nbsp; BOOST_CHECK( add( 2,2 ) == 4 );&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // #1 continues on error<br>&nbsp;&nbsp;&nbsp; BOOST_REQUIRE( add( 2,2 ) == 4 );&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // #2 throws on error<br>&nbsp;&nbsp;&nbsp; if( add( 2,2 ) != 4 )<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; BOOST_ERROR( "Ouch..." );&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // #3 continues on error<br>&nbsp;&nbsp;&nbsp; if( add( 2,2 ) != 4 )<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; BOOST_FAIL( "Ouch..." );&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // #4 throws on error<br>&nbsp;&nbsp;&nbsp; if( add( 2,2 ) != 4 ) throw "Oops..."; // #5 throws on error<br>&nbsp;&nbsp;&nbsp; return add( 2, 2 ) == 4 ? 0 : 1;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // #6 returns error code<br>}<br>BOOST_CHECK 如果该表达式失败了，会出现源代码文件名、代码行号、并且会增加 error count，一旦程序终止 error count 会显示在 std::cout；<br>BOOST_REQUIRE 类似于 BOOST_CHECK，但是会抛出被&nbsp; Minimal testing facility 捕获的异常。<br>(http://yaekees.spaces.live.com/blog/cns!1955EE8C6707277A!147.entry)<br><br>
<img src ="http://www.cppblog.com/shuiyuan2004/aggbug/45677.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/shuiyuan2004/" target="_blank">不倦</a> 2008-03-29 15:39 <a href="http://www.cppblog.com/shuiyuan2004/archive/2008/03/29/45677.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>CxxTest使用指南（入门篇）</title><link>http://www.cppblog.com/shuiyuan2004/archive/2007/08/31/31322.html</link><dc:creator>不倦</dc:creator><author>不倦</author><pubDate>Fri, 31 Aug 2007 10:42:00 GMT</pubDate><guid>http://www.cppblog.com/shuiyuan2004/archive/2007/08/31/31322.html</guid><wfw:comment>http://www.cppblog.com/shuiyuan2004/comments/31322.html</wfw:comment><comments>http://www.cppblog.com/shuiyuan2004/archive/2007/08/31/31322.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/shuiyuan2004/comments/commentRss/31322.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/shuiyuan2004/services/trackbacks/31322.html</trackback:ping><description><![CDATA[&nbsp;
<p><span>CxxTest</span><span>使用指南（入门篇）</span></p>
<p><span>准备工作：</span></p>
<p><span><span>1、&nbsp;</span></span><span>安装</span><span>perl/Python</span></p>
<p><span><span>2、&nbsp;</span></span><span>下载解压</span><span>CxxTest</span></p>
<p><span><span>3、&nbsp;</span></span><span>设置环境变量：</span></p>
<p align=left><span>如果使用<span>Perl,</span>则设置一个名为<span>PERL</span>的环境变量，值为<span>perl.exe</span>的位置。（比如<span>D:\Perl\Bin\Perl.exe</span>）；</span></p>
<p align=left><span>如果安装<span>Python,</span>则设置一个名为<span>PYTHON</span>的环境变量，值为你安装的<span>Python</span>目录下的<span>python.exe</span>路径（比如<span>D:\Python25\python.exe</span>）；</span></p>
<p align=left><span>设置一个名为<span>CXXTESTDIR </span>的环境变量，值为<span>CxxTest</span>解压后的目录。（比如<span>D:\Cxxtest</span>）。（设置<span>CXXTESTDIR</span>的原因：免去每次在<span>makefile</span>文件中指定<span>CXXTESTDIR</span>的目录）</span></p>
<p align=left>&nbsp;</p>
<p align=left><span>方式一：使用<span>CxxTest</span></span><span> </span><span>\sample\msvc</span><span>中的框架</span></p>
<p align=left><span><span>1、&nbsp;</span></span><span>拷贝<span>msvc</span>文件夹到工程目录中</span></p>
<p align=left><span>打开<span>CxxTest_Workspace.sln</span>，可以看到三个项目：</span></p>
<p align=left><span>- CxxTest_3_Generate </span><span>运行</span><span> cxxtestgen.pl </span><span>生成</span><span> runner.cpp </span><span>文件；</span></p>
<p align=left><span>- CxxTest_2_Build </span><span>编译生成的</span><span>runner.cpp</span><span>文件；</span></p>
<p align=left><span>- CxxTest_1_Run </span><span>运行测试；</span></p>
<p align=left><span><span>2、&nbsp;</span></span><span>修改其中的<span>makefile</span>文件<span>:</span></span></p>
<p align=left><span>1</span><span>）将以下内容：</span></p>
<p align=left><span># Where to look for the tests</span></p>
<p align=left><span>TESTS<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>= ..\gui\*.h ..\*.h</span></p>
<p align=left><span>修改为：</span></p>
<p align=left><span># Where to look for the tests</span></p>
<p align=left><span>TESTS<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>= *.h</span></p>
<p align=left><span>这段话的作用是查找测试文件，我们的测试文件是<span>.h</span>格式的，放在当前目录下。（如果是<span>.hpp</span>格式的话，自然改成<span>*.hpp</span>；放在其它目录的话，还要修改路径）</span></p>
<p align=left><span>2</span><span>）将以下内容删除：</span></p>
<p align=left><span># Where the CxxTest distribution is unpacked</span></p>
<p align=left><span>CXXTESTDIR<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>= ..\..</span></p>
<p align=left><span>因为先前已经定义了<span>CXXTESTDIR</span>的环境变量，这里的定义可以省掉。</span></p>
<p align=left><span><span>3、&nbsp;</span></span><span>在</span><span>CxxTest_3_Generate</span><span>项目中添加测试文件</span></p>
<p align=left><span>这是测试文件的一个简单的例子：</span></p>
<p align=left><span>// Sampletest.h</span></p>
<p align=left><span>#include</span><span> <span>&lt;cxxtest/TestSuite.h&gt;</span></span></p>
<p align=left><span>//</span><span>定义一个测试套件类，将测试用例放入其中</span></p>
<p align=left><span>class</span><span> <span>SampletestSuite</span> : <span>public</span> <span>CxxTest</span>::<span>TestSuite</span><span>&nbsp;&nbsp;&nbsp; </span></span></p>
<p align=left><span>{</span></p>
<p align=left><span>public</span><span>:</span></p>
<p align=left><span><span>&nbsp;&nbsp;&nbsp;&nbsp; </span><span>//</span></span><span>定义测试，以<span>test</span>作为测试函数前缀，</span></p>
<p align=left><span><span>&nbsp;&nbsp;&nbsp;&nbsp; </span><span>//</span></span><span>这是<span>cxxtestgen.pl</span>或<span>cxxtestgen.py</span>对测试文件进行扫描，抽取测试用例的依据</span></p>
<p align=left><span><span>&nbsp;&nbsp;&nbsp;&nbsp; </span><span>void</span> <span>testMultiplication</span>( <span>void</span> )<span>&nbsp;&nbsp;&nbsp; </span></span></p>
<p align=left><span><span>&nbsp;&nbsp;&nbsp;&nbsp; </span>{</span></p>
<p align=left><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span>TS_ASSERT_EQUALS</span>( 2 * 2, 5 );</span></p>
<p align=left><span><span>&nbsp;&nbsp;&nbsp;&nbsp; </span>}</span></p>
<p><span>};</span></p>
<p><span><span>4、&nbsp;</span></span><span>运行测试，即生成</span><span>CxxTest_1_Run</span><span>，可在输出窗口看到测试结果，并双击结果行可定位到源代码。</span></p>
<p>&nbsp;</p>
<p><span>方法二：简化<span>msvc</span>，将</span><span>Generate</span><span>，</span><span>Build</span><span>，</span><span>Run</span><span>集成到一个项目中</span></p>
<p><span><span>1、&nbsp;</span></span><span>在工程中添加新项目：</span><span>Unit</span><span>_<span>Test</span></span></p>
<p><span>将修改后的<span>makefile</span>文件拷贝到<span>Unit_Test</span>目录中。</span></p>
<p><span><span>2、&nbsp;</span></span><span>在<span>Unit_Test</span>项目源文件中添加新建项<span>dummy</span></span></p>
<p><span>无论添加进来的是<span>.cpp,</span>还是<span>.h,</span>将后缀名去掉。</span></p>
<p><span>右击<span>dummy</span>，选择属性，在弹出的属性页中，&#8220;自定义生成步骤&#8221;选项<span>-&gt;</span>&#8220;命令行&#8221;选项，输入：</span></p>
<p><span>ECHO Don't Delete this file!!! &gt; Dummy</span></p>
<p><span>ECHO -</span></p>
<p><span>ECHO -</span></p>
<p><span>ECHO -------------------- Generating test cases --------------------</span></p>
<p><span>if exist runner.cpp <st1:place w:st="on"><st1:state w:st="on">del</st1:state></st1:place> runner.cpp /Q</span></p>
<p><span>NMAKE runner.cpp /nologo</span></p>
<p><span>ECHO -</span></p>
<p><span>ECHO &#8211;</span></p>
<p>&nbsp;</p>
<p><span>在&#8220;输出&#8221;选项中输入：</span></p>
<p><span>Runner.cpp</span></p>
<p>&nbsp;</p>
<p align=left><span>Dummy</span><span>只是一个文件，里边可以是任何内容，</span><span>关键点在于对<span>Dummy</span>文件进行编译的时候所执行的操作，这是一个批处理文件，这个批处理文件执行生成<span> testcase </span>的操作，也就是调用<span>Python/Perl</span>生成测试用例（也就是<span>runner.cpp</span>文件）。为了保持每次编译都可以生成新的<span>runner</span>文件，就必须保证</span></p>
<p align=left><span>1</span><span>）<span>Dummy</span>文件在<span>runner.cpp</span>之前进行编译；</span></p>
<p align=left><span>2</span><span>）<span>Dummy</span>文件每次都必须被重新编译。</span>&nbsp;</p>
<p align=left><span>第一点是通过文件顺序来的，第二点是在生成事件里边重新生成<span>Dummy</span>文件来保证的（<span>VS2005</span>似乎没问题，<span>VC6</span>似乎还是有些问题的）。</span></p>
<p align=left><span>从另外一个角度讲，这个<span>Dummy</span>文件相当于<span> samples/msvc </span>第一和第二个项目的功能。（即</span><span>Generate</span><span>和</span><span>Build</span><span>）</span></p>
<p align=left>&nbsp;</p>
<p><span><span>3、&nbsp;</span></span><span>编译<span>dummy</span>文件，生成<span>runner.cpp</span>，将其添加到<span>Unit_Test</span>&#8220;源文件&#8221;中。</span></p>
<p><span>确定当前目录中已有<span>.h</span>测试文件，否则可能提示错误。</span></p>
<p><span><span>4、&nbsp;</span></span><span>右击<span>Unit_Test</span>，选择属性，在弹出来的属性页中， &#8220;生成事件&#8221;选项<span>-&gt;</span>&#8220;生成后事件<span>&#8221;</span>选项<span>-&gt;</span>&#8220;命令行&#8221;选项中输入：</span></p>
<p><span>ECHO -</span></p>
<p><span>ECHO -</span></p>
<p><span>ECHO -------------------- Running Unit Test Cases --------------------</span></p>
<p><span>$(OutDir)\$(TargetName).exe</span></p>
<p><span>ECHO -</span></p>
<p><span>ECHO -</span></p>
<p><span>ECHO -------------------------------------------------------------------------------</span></p>
<p><st1:place w:st="on"><st1:state w:st="on"><span>DEL</span></st1:state></st1:place><span> runner.cpp</span></p>
<p>&nbsp;</p>
<p><span>放在项目&#8220;生成后事件&#8221;中的这一段命令，运行生成的测试，相当于</span><span>samples/msvc </span><span>第三个项目的功能。（即</span><span>Run</span><span>）</span></p>
<p>&nbsp;</p>
<p><span><span>5、&nbsp;</span></span><span>在<span>Unit_Test</span>&#8220;头文件&#8221;中，添加新的<span>.h</span>测试文件。</span></p>
<p><span>生成<span>Unit_Test</span>，即可在输出窗口分割线下看到运行测试的结果，双击结果行可以定位到源代码。</span></p>
<p><span>如：</span></p>
<p align=left><span>1&gt;-------------------- Running Unit Test Cases --------------------</span></p>
<p align=left><span>1&gt;Running 1 test</span></p>
<p align=left><span>1&gt;In MathsTestSuite::testMultiplication:</span></p>
<p align=left><span>1&gt;f:\test\cxxunittest\cxxunittest\sampletest.h(9): Error: Expected (2 * 2 == 5), found (4 != 5)</span></p>
<p align=left><span>1&gt;Failed 1 of 1 test</span></p>
<p align=left><span>1&gt;Success rate: 0%</span></p>
<p><span>1&gt;----------------------------------------------------------------------------------</span></p>
<p>&nbsp;</p>
<p><span>方法三：抽取出一个<span>Unit_Test</span>文件夹</span></p>
<p><span>所谓方法三，不过是将方法二中的<span>Unit_Test</span>文件夹，替代方法一中的<span>msvc</span>文件夹，移动到其它的项目中使用而已。</span></p>
<p><span>不像方法一的<span>msvc</span>那样需要三个项目，也不需像方法二那样，每次都修改<span>dummy</span>，和项目属性，添加命令行。将<span>Unit_Test</span>文件夹各个属性都配置好之后，独立出来随时备用。</span></p>
<p>&nbsp;</p>
<p><span>准备工作，如前所言。</span></p>
<p><span>要进行测试时：</span></p>
<p><span>将</span><span>Unit_test</span><span>文件夹拷贝放到工程目录中，在工程中添加里面的</span><span>Unit_Test</span><span>项目，仿照</span><span>Sampletest.h</span><span>写自己的测试文件。</span></p>
<p><span>运行测试时：</span></p>
<span>先编译</span><span>dummy</span><span>，再生成该测试项目。</span><span>(</span><span>如果没有另外添加</span><span>*.h</span><span>文件，直接生成就可以了</span><span>) </span>
<img src ="http://www.cppblog.com/shuiyuan2004/aggbug/31322.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/shuiyuan2004/" target="_blank">不倦</a> 2007-08-31 18:42 <a href="http://www.cppblog.com/shuiyuan2004/archive/2007/08/31/31322.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>C++单元测试框架的比较[me]</title><link>http://www.cppblog.com/shuiyuan2004/archive/2007/08/29/31105.html</link><dc:creator>不倦</dc:creator><author>不倦</author><pubDate>Wed, 29 Aug 2007 01:30:00 GMT</pubDate><guid>http://www.cppblog.com/shuiyuan2004/archive/2007/08/29/31105.html</guid><wfw:comment>http://www.cppblog.com/shuiyuan2004/comments/31105.html</wfw:comment><comments>http://www.cppblog.com/shuiyuan2004/archive/2007/08/29/31105.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/shuiyuan2004/comments/commentRss/31105.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/shuiyuan2004/services/trackbacks/31105.html</trackback:ping><description><![CDATA[&nbsp;
<p><span>C++</span><span>单元测试框架的比较</span></p>
<p><span>单元测试现在已经成为标准的编程实践，但是</span><span>C++</span><span>缺少</span><span>Java</span><span>和</span><span>.Net</span><span>平台语言的反射机制，所以无法枚举测试方法，必须手工添加，或者使用一些特别的宏，弄得代码非常难看。</span><span>Java</span><span>语言单元测试是</span><span>JUnit</span><span>的天下，</span><span>C#</span><span>基本上都用</span><span>NUnit</span><span>，而</span><span>C++</span><span>则群花怒放，单元测试框架非常多，</span><span>JUnit</span><span>移植过来的</span><span>CppUnit</span><span>，</span><span>Boost::test</span><span>，</span><span>CppTest</span><span>，</span><span>CxxTest</span><span>，</span><span> TUT</span><span>等等。但是解决方案最好的是</span><span>CxxTest</span><span>和</span><span>TUT</span><span>，</span><span>CxxTest</span><span>采用的方法比较特殊，用</span><span>Perl</span><span>分析</span><span>C++</span><span>的源文件，从中抽取测试方法，创建</span><span>TestSuite</span><span>。语法与</span><span>JUnit</span><span>非常相似，没有使用高级的</span><span>C++</span><span>特性，也没有定义特别的宏，无须写额外的代码。</span><span>TUT</span><span>也是一个不错的解决方案，利用高级</span><span>C++ Template</span><span>功能，必须比较新的编译器才支持，比如</span><span>VC6</span><span>和</span><span>VS.NET 2002</span><span>就不支持，必须</span><span>VS.NET 2003</span><span>以上或者</span><span>Intel C++ Complier 8.1</span><span>以上。</span></p>
<p>&nbsp;</p>
<p><span><span>1、&nbsp;</span></span><span>TUT</span></p>
<p><span>结构框架简单。添加新的测试工作量小；无须注册测试；可移植性好（因其只需两个头文件，就可以完成测试工作）；便于装卸；提供接口可以扩展其输出方式等。</span></p>
<p><span>最大的优点：轻量级，便于装卸和可扩展其输出方式；</span></p>
<p><span>缺点：断言似乎不是很好，只用了一个</span><span>ensure()</span><span>函数，不知道对复杂的测试是否支持；输出的测试结果较为简单。</span></p>
<p><span><span>2、&nbsp;</span></span><span>Boost::test</span></p>
<p><span>结构框架较为复杂。添加新的测试工作量也不大；提供多种测试方法，可注册测试用例，也可不注册；可移植性一般；装卸不易；在控制异常、崩溃方面的能力胜过其它所有对手；拥有良好的断言功能；大概能支持多种输出方式，但更改输出方式不易；支持测试套件。</span></p>
<p><span>最大的优点：控制异常崩溃的能力、良好的断言、输出结果较为详细、编写测试的方法灵活；</span></p>
<p><span>缺点：结构框架较为复杂，更改输出方式不易，装卸不易。</span></p>
<p><span><span>3、&nbsp;</span></span><span>CXXTest</span></p>
<p><span>结构框架的复杂性处于</span><span>TUT</span><span>与</span><span>boost::test</span><span>之间。添加新的测试工作量非常小；无须注册测试用例；可移植性很好；便于装卸；控制异常、崩溃方面的能力也不错；拥有良好的断言功能；支持多种输出方式；支持测试套件。</span></p>
<p><span>最大的优点：</span><span>编译即测试方式，并且可以双击结果行立即定位到相应的源代码，相当吸引人；支持多种输出，输出结果较为详细；编写测试简单；</span></p>
<p><span>缺点：需要用到</span><span>perl</span><span>对测试代码进行文法扫描，生成可执行代码，需要用到</span><span>makefile</span><span>文件（不是必须）；准备工作比较麻烦。</span></p>
<img src ="http://www.cppblog.com/shuiyuan2004/aggbug/31105.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/shuiyuan2004/" target="_blank">不倦</a> 2007-08-29 09:30 <a href="http://www.cppblog.com/shuiyuan2004/archive/2007/08/29/31105.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>C++测试框架的选择[转]</title><link>http://www.cppblog.com/shuiyuan2004/archive/2007/08/26/30852.html</link><dc:creator>不倦</dc:creator><author>不倦</author><pubDate>Sun, 26 Aug 2007 06:34:00 GMT</pubDate><guid>http://www.cppblog.com/shuiyuan2004/archive/2007/08/26/30852.html</guid><wfw:comment>http://www.cppblog.com/shuiyuan2004/comments/30852.html</wfw:comment><comments>http://www.cppblog.com/shuiyuan2004/archive/2007/08/26/30852.html#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://www.cppblog.com/shuiyuan2004/comments/commentRss/30852.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/shuiyuan2004/services/trackbacks/30852.html</trackback:ping><description><![CDATA[<div class=cnt>
<p><font color=#800080><a href="http://www.gamesfromwithin.com/articles/0412/000061.html" target=_blank><u><font color=#0000ff>http://www.gamesfromwithin.com/articles/0412/000061.html</font></u></a>，</font>在这篇文章中，Noel Llopis提出了一个对C++ test framework评判的一些依据，按照Noel Llopis给出的重要性，我节略在这里。</p>
<p>1.加入新测试最小化工作量</p>
<p>2.便于修改和移植（作者的意思是说比如RTTI，STL，Exception这些高级特性可能妨碍在不同的平台，不同版本编译器下面的可移植性）</p>
<p>3.便于装配/拆卸测试环境</p>
<p>4.对异常以及崩溃很好的控制</p>
<p>5.好的断言功能</p>
<p>6.支持不同的输出方式</p>
<p>7.支持测试套件(suites)</p>
<p>&nbsp;</p>
<p>按照这个标准，Noel Llopis对下面的test framework进行了评价</p>
<p>CPPUnit</p>
<p>1.工作量多</p>
<p>2.CPPUnit能在Windows , Linux上面运行，功能进行了很好的模块化，但是另一方面，CPPUnit需要RTTI，STL，或者异常（作者不是很肯定）</p>
<p>3.</p>
<p>4.CPPUnit使用protectors包装测试，并且捕捉所有的异常（尝试识别某些异常），Linux下面不会捕捉系统异常，但是要增加自定义的包装是很容易的。</p>
<p>5.很好，支持一个最小集合的断言语句，包括比较浮点数。</p>
<p>6.支持</p>
<p>7.支持</p>
<p>总体评价：Overall, CppUnit is frustrating because it's almost exactly what I want, except for my most wanted feature. (CPPUnit够闷的，不过我觉得改进易用性应该可以期待）</p>
<p>&nbsp;</p>
<p>Boost.Test（我尝试使用，在VC.Net 2003下面遇到链接问题，还没有解决）</p>
<p>1.基本满足</p>
<p>2.和CPPUnit类似，但强调的是改代码的难度以及依赖Boost本身</p>
<p>3.避开了常规的setup/teardown结构，可以不需要动态生成fixture 对象，可以将fixture对象放到stack里面。</p>
<p>4.Boost.Test在这方面超过了所有的其他竞争对手</p>
<p>5.Yes</p>
<p>6.大概能支持，但改变输出这件事情并不是很容易</p>
<p>7.支持，...（这句如何理解？Yes, but with a big catch）</p>
<p>Overall,Boost.Test is a library with a huge amount of potential. It has great support for exception handling and advanced assert statements. It also has other fairly unique functionality such as support for checking for infinite loops, and different levels of logging. On the other hand, it's very verbose to add new tests that are part of a suite, and it might be a bit heavy weight for game console environments.</p>
<p>&nbsp;</p>
<p>CppUnitLite(由于作者比较了一个被他改动的版本，我不再关注）</p>
<p>&nbsp;</p>
<p>NanoCppUnit（这个库甚至需要你去从web pages上面copy代码，然后自己搞一个工程，我觉得我不太喜欢这种方式的package发布，毕竟，我希望少操心，所以我也不关注）</p>
<p>&nbsp;</p>
<p>Unit++</p>
<p>首先指出一个独特的特性：More C++ Like，作者的意思是它没有使用宏，的确，前面几种framework开始一个测试的时候都使用了宏，这在许多C++ Library中是惯例，用来简化一些代码。我们通过从基类继承从而创建测试包，当然在其他framework里面本质也是这样，但是都放在幕后进行，宏掩盖了具体情况。</p>
<p>1.不好</p>
<p>2.一般般</p>
<p>3.不支持</p>
<p>4.表现平均</p>
<p>5.文档没说如何支持不同的输出</p>
<p>6.不支持浮点数</p>
<p>7.支持</p>
<p>&nbsp;</p>
<p>CxxTest</p>
<p>首先作者认为文档最好（很重要？）另外作者指出,CxxTest的作者Erez Volk意识到我们是在写工具帮助测试C++程序，所以不必受限于C++的特征。 </p>
<p>1.非常好</p>
<p>2.很好</p>
<p>3.支持</p>
<p>4.很好</p>
<p>5.yes</p>
<p>6.yes</p>
<p>7.yes</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>文章最后给出一个综述：是个表现好的，CPPUnit, CppUnitLite, Boost.Test, CxxTest，作者本人喜欢CxxTest.</p>
<p>(完) </p>
<p>&nbsp;</p>
</div>
转自：<a href="http://hi.baidu.com/fangfang%5Fi/blog/item/32876bfb5d140a64024f56e6.html"><u><font color=#0000ff>http://hi.baidu.com/fangfang%5Fi/blog/item/32876bfb5d140a64024f56e6.html</font></u></a> 
<img src ="http://www.cppblog.com/shuiyuan2004/aggbug/30852.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/shuiyuan2004/" target="_blank">不倦</a> 2007-08-26 14:34 <a href="http://www.cppblog.com/shuiyuan2004/archive/2007/08/26/30852.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>全面介绍单元测试 －转贴</title><link>http://www.cppblog.com/shuiyuan2004/archive/2007/08/26/30851.html</link><dc:creator>不倦</dc:creator><author>不倦</author><pubDate>Sun, 26 Aug 2007 06:33:00 GMT</pubDate><guid>http://www.cppblog.com/shuiyuan2004/archive/2007/08/26/30851.html</guid><wfw:comment>http://www.cppblog.com/shuiyuan2004/comments/30851.html</wfw:comment><comments>http://www.cppblog.com/shuiyuan2004/archive/2007/08/26/30851.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/shuiyuan2004/comments/commentRss/30851.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/shuiyuan2004/services/trackbacks/30851.html</trackback:ping><description><![CDATA[<span class=smalltxt><span class=bold><font size=2>
<p><font size=2><span class=smalltxt><span class=bold>全面介绍单元测试 －转贴</span></span><br><br></font><span style="FONT-SIZE: 12px"><font size=2>这是一篇全面介绍单元测试的经典之作，对理解单元测试和Visual Unit很有帮助，作者老纳，收录时作了少量修改<br><br>一　单元测试概述<br>　　工厂在组装一台电视机之前，会对每个元件都进行测试，这，就是单元测试。<br>　　其实我们每天都在做单元测试。你写了一个函数，除了极简单的外，总是要执行一下，看看功能是否正常，有时还要想办法输出些数据，如弹出信息窗口什么的，这，也是单元测试，老纳把这种单元测试称为临时单元测试。只进行了临时单元测试的软件，针对代码的测试很不完整，代码覆盖率要超过70%都很困难，未覆盖的代码可能遗留大量的细小的错误，这些错误还会互相影响，当BUG暴露出来的时候难于调试，大幅度提高后期测试和维护成本，也降低了开发商的竞争力。可以说，进行充分的单元测试，是提高软件质量，降低开发成本的必由之路。<br>　　对于程序员来说，如果养成了对自己写的代码进行单元测试的习惯，不但可以写出高质量的代码，而且还能提高编程水平。<br>　　要进行充分的单元测试，应专门编写测试代码，并与产品代码隔离。老纳认为，比较简单的办法是为产品工程建立对应的测试工程，为每个类建立对应的测试类，为每个函数（很简单的除外）建立测试函数。首先就几个概念谈谈老纳的看法。<br>　　一般认为，在结构化程序时代，单元测试所说的单元是指函数，在当今的面向对象时代，单元测试所说的单元是指类。以老纳的实践来看，以类作为测试单位，复杂度高，可操作性较差，因此仍然主张以函数作为单元测试的测试单位，但可以用一个测试类来组织某个类的所有测试函数。单元测试不应过分强调面向对象，因为局部代码依然是结构化的。单元测试的工作量较大，简单实用高效才是硬道理。<br>　　有一种看法是，只测试类的接口(公有函数)，不测试其他函数，从面向对象角度来看，确实有其道理，但是，测试的目的是找错并最终排错，因此，只要是包含错误的可能性较大的函数都要测试，跟函数是否私有没有关系。对于C++来说，可以用一种简单的方法区隔需测试的函数：简单的函数如数据读写函数的实现在头文件中编写(inline函数)，所有在源文件编写实现的函数都要进行测试(构造函数和析构函数除外)。<br>　　什么时候测试？单元测试越早越好，早到什么程度？XP开发理论讲究TDD，即测试驱动开发，先编写测试代码，再进行开发。在实际的工作中，可以不必过分强调先什么后什么，重要的是高效和感觉舒适。从老纳的经验来看，先编写产品函数的框架，然后编写测试函数，针对产品函数的功能编写测试用例，然后编写产品函数的代码，每写一个功能点都运行测试，随时补充测试用例。所谓先编写产品函数的框架，是指先编写函数空的实现，有返回值的随便返回一个值，编译通过后再编写测试代码，这时，函数名、参数表、返回类型都应该确定下来了，所编写的测试代码以后需修改的可能性比较小。<br>　　由谁测试？单元测试与其他测试不同，单元测试可看作是编码工作的一部分，应该由程序员完成，也就是说，经过了单元测试的代码才是已完成的代码，提交产品代码时也要同时提交测试代码。测试部门可以作一定程度的审核。<br>　　关于桩代码，老纳认为，单元测试应避免编写桩代码。桩代码就是用来代替某些代码的代码，例如，产品函数或测试函数调用了一个未编写的函数，可以编写桩函数来代替该被调用的函数，桩代码也用于实现测试隔离。采用由底向上的方式进行开发，底层的代码先开发并先测试，可以避免编写桩代码，这样做的好处有：减少了工作量；测试上层函数时，也是对下层函数的间接测试；当下层函数修改时，通过回归测试可以确认修改是否导致上层函数产生错误。<br><br>二　测试代码编写<br>　　多数讲述单元测试的文章都是以Java为例，本文以C++为例，后半部分所介绍的单元测试工具也只介绍C++单元测试工具。下面的示例代码的开发环境是VC6.0。<br><br>产品类：<br>class CMyClass <br>{<br>public:<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int Add(int i, int j);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; CMyClass();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; virtual ~CMyClass();<br><br>private:<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int mAge;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //年龄<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; CString mPhase; //年龄阶段，如"少年"，"青年"<br>};<br><br>建立对应的测试类CMyClassTester，为了节约编幅，只列出源文件的代码：<br>void CMyClassTester::CaseBegin()<br>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //pObj是CMyClassTester类的成员变量，是被测试类的对象的指针，<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //为求简单，所有的测试类都可以用pObj命名被测试对象的指针。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pObj = new CMyClass();<br>}<br><br>void CMyClassTester::CaseEnd()<br>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; delete pObj;<br>}<br>测试类的函数CaseBegin()和CaseEnd()建立和销毁被测试对象，每个测试用例的开头都要调用CaseBegin()，结尾都要调用CaseEnd()。<br><br>接下来，我们建立示例的产品函数：<br>int CMyClass::Add(int i, int j)<br>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return i+j;<br>}<br>和对应的测试函数：<br>void CMyClassTester::Add_int_int()<br>{<br>}<br>把参数表作为函数名的一部分，这样当出现重载的被测试函数时，测试函数不会产生命名冲突。下面添加测试用例：<br>void CMyClassTester::Add_int_int()<br>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //第一个测试用例<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; CaseBegin();{&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //1<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int i = 0;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //2<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int j = 0;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //3<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int ret = pObj-&gt;Add(i, j); //4<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ASSERT(ret == 0);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //5<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }CaseEnd();&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //6<br>}<br>第1和第6行建立和销毁被测试对象，所加的{}是为了让每个测试用例的代码有一个独立的域，以便多个测试用例使用相同的变量名。<br>第2和第3行是定义输入数据，第4行是调用被测试函数，这些容易理解，不作进一步解释。第5行是预期输出，它的特点是当实际输出与预期输出不同时自动报错，ASSERT是VC的断言宏，也可以使用其他类似功能的宏，使用测试工具进行单元测试时，可以使用该工具定义的断言宏。<br><br>　　示例中的格式显得很不简洁，2、３、4、5行可以合写为一行：ASSERT(pObj-&gt;Add(0, 0) == 0);但这种不简洁的格式却是老纳极力推荐的，因为它一目了然，易于建立多个测试用例，并且具有很好的适应性，同时，也是极佳的代码文档，总之，老纳建议：输入数据和预期输出要自成一块。<br>　　建立了第一个测试用例后，应编译并运行测试，以排除语法错误，然后，使用拷贝/修改的办法建立其他测试用例。由于各个测试用例之间的差别往往很小，通常只需修改一两个数据，拷贝/修改是建立多个测试用例的最快捷办法。<br><br>三　测试用例<br>　　下面说说测试用例、输入数据及预期输出。输入数据是测试用例的核心，老纳对输入数据的定义是：被测试函数所读取的外部数据及这些数据的初始值。外部数据是对于被测试函数来说的，实际上就是除了局部变量以外的其他数据，老纳把这些数据分为几类：参数、成员变量、全局变量、IO媒体。IO媒体是指文件、数据库或其他储存或传输数据的媒体，例如，被测试函数要从文件或数据库读取数据，那么，文件或数据库中的原始数据也属于输入数据。一个函数无论多复杂，都无非是对这几类数据的读取、计算和写入。预期输出是指：返回值及被测试函数所写入的外部数据的结果值。返回值就不用说了，被测试函数进行了写操作的参数(输出参数)、成员变量、全局变量、IO媒体，它们的预期的结果值都是预期输出。一个测试用例，就是设定输入数据，运行被测试函数，然后判断实际输出是否符合预期。下面举一个与成员变量有关的例子：<br>产品函数：<br>void CMyClass::Grow(int years)<br>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mAge += years;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if(mAge &lt; 10)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mPhase = "儿童";<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else if(mAge &lt;20)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mPhase = "少年";<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else if(mAge &lt;45)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mPhase = "青年";<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else if(mAge &lt;60)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mPhase = "中年";<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mPhase = "老年";<br>}<br><br>测试函数中的一个测试用例：<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; CaseBegin();{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int years = 1;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pObj-&gt;mAge = 8;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pObj-&gt;Grow(years);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ASSERT( pObj-&gt;mAge == 9 );<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ASSERT( pObj-&gt;mPhase == "儿童" );<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }CaseEnd();<br>在输入数据中对被测试类的成员变量mAge进行赋值，在预期输出中断言成员变量的值。现在可以看到老纳所推荐的格式的好处了吧，这种格式可以适应很复杂的测试。在输入数据部分还可以调用其他成员函数，例如：执行被测试函数前可能需要读取文件中的数据保存到成员变量，或需要连接数据库，老纳把这些操作称为初始化操作。例如，上例中 ASSERT( ...)之前可以加pObj-&gt;OpenFile();。为了访问私有成员，可以将测试类定义为产品类的友元类。例如，定义一个宏：<br>#define UNIT_TEST(cls) friend class cls##Tester;<br>然后在产品类声明中加一行代码：UNIT_TEST(ClassName)。<br><br>　　下面谈谈测试用例设计。前面已经说了，测试用例的核心是输入数据。预期输出是依据输入数据和程序功能来确定的，也就是说，对于某一程序，输入数据确定了，预期输出也就可以确定了，至于生成/销毁被测试对象和运行测试的语句，是所有测试用例都大同小异的，因此，我们讨论测试用例时，只讨论输入数据。<br>　　前面说过，输入数据包括四类：参数、成员变量、全局变量、IO媒体，这四类数据中，只要所测试的程序需要执行读操作的，就要设定其初始值，其中，前两类比较常用，后两类较少用。显然，把输入数据的所有可能取值都进行测试，是不可能也是无意义的，我们应该用一定的规则选择有代表性的数据作为输入数据，主要有三种：正常输入，边界输入，非法输入，每种输入还可以分类，也就是平常说的等价类法，每类取一个数据作为输入数据，如果测试通过，可以肯定同类的其他输入也是可以通过的。下面举例说明： <br>　　正常输入<br>　　例如字符串的Trim函数，功能是将字符串前后的空格去除，那么正常的输入可以有四类：前面有空格；后面有空格；前后均有空格；前后均无空格。<br>　　边界输入<br>　　上例中空字符串可以看作是边界输入。<br>　　再如一个表示年龄的参数，它的有效范围是0-100，那么边界输入有两个：0和100。<br>　　非法输入<br>　　非法输入是正常取值范围以外的数据，或使代码不能完成正常功能的输入，如上例中表示年龄的参数，小于0或大于100都是非法输入，再如一个进行文件操作的函数，非法输入有这么几类：文件不存在；目录不存在；文件正在被其他程序打开；权限错误。<br>　　如果函数使用了外部数据，则正常输入是肯定会有的，而边界输入和非法输入不是所有函数都有。一般情况下，即使没有设计文档，考虑以上三种输入也可以找出函数的基本功能点。实际上，单元测试与代码编写是&#8220;一体两面&#8221;的关系，编码时对上述三种输入都是必须考虑的，否则代码的健壮性就会成问题。<br><br>四　白盒覆盖<br>　　上面所说的测试数据都是针对程序的功能来设计的，就是所谓的黑盒测试。单元测试还需要从另一个角度来设计测试数据，即针对程序的逻辑结构来设计测试用例，就是所谓的白盒测试。在老纳看来，如果黑盒测试是足够充分的，那么白盒测试就没有必要，可惜&#8220;足够充分&#8221;只是一种理想状态，例如：真的是所有功能点都测试了吗？程序的功能点是人为的定义，常常是不全面的；各个输入数据之间，有些组合可能会产生问题，怎样保证这些组合都经过了测试？难于衡量测试的完整性是黑盒测试的主要缺陷，而白盒测试恰恰具有易于衡量测试完整性的优点，两者之间具有极好的互补性，例如：完成功能测试后统计语句覆盖率，如果语句覆盖未完成，很可能是未覆盖的语句所对应的功能点未测试。<br>　　白盒测试针对程序的逻辑结构设计测试用例，用逻辑覆盖率来衡量测试的完整性。逻辑单位主要有：语句、分支、条件、条件值、条件值组合，路径。语句覆盖就是覆盖所有的语句，其他类推。另外还有一种判定条件覆盖，其实是分支覆盖与条件覆盖的组合，在此不作讨论。跟条件有关的覆盖就有三种，解释一下：条件覆盖是指覆盖所有的条件表达式，即所有的条件表达式都至少计算一次，不考虑计算结果；条件值覆盖是指覆盖条件的所有可能取值，即每个条件的取真值和取假值都要至少计算一次；条件值组合覆盖是指覆盖所有条件取值的所有可能组合。老纳做过一些粗浅的研究，发现与条件直接有关的错误主要是逻辑操作符错误，例如：||写成&amp;&amp;，漏了写!什么的，采用分支覆盖与条件覆盖的组合，基本上可以发现这些错误，另一方面，条件值覆盖与条件值组合覆盖往往需要大量的测试用例，因此，在老纳看来，条件值覆盖和条件值组合覆盖的效费比偏低。老纳认为效费比较高且完整性也足够的测试要求是这样的：完成功能测试，完成语句覆盖、条件覆盖、分支覆盖、路径覆盖。做过单元测试的朋友恐怕会对老纳提出的测试要求给予一个字的评价：晕！或者两个字的评价：狂晕！因为这似乎是不可能的要求，要达到这种测试完整性，其测试成本是不可想象的，不过，出家人不打逛语，老纳之所以提出这种测试要求，是因为利用一些工具，可以在较低的成本下达到这种测试要求，后面将会作进一步介绍。<br>　　关于白盒测试用例的设计，程序测试领域的书籍一般都有讲述，普通方法是画出程序的逻辑结构图如程序流程图或控制流图，根据逻辑结构图设计测试用例，这些是纯粹的白盒测试，不是老纳想推荐的方式。老纳所推荐的方法是：先完成黑盒测试，然后统计白盒覆盖率，针对未覆盖的逻辑单位设计测试用例覆盖它，例如，先检查是否有语句未覆盖，有的话设计测试用例覆盖它，然后用同样方法完成条件覆盖、分支覆盖和路径覆盖，这样的话，既检验了黑盒测试的完整性，又避免了重复的工作，用较少的时间成本达到非常高的测试完整性。不过，这些工作可不是手工能完成的，必须借助于工具，后面会介绍可以完成这些工作的测试工具。<br><br>五　单元测试工具<br>　　现在开始介绍单元测试工具，老纳只介绍三种，都是用于C++语言的。<br>　　首先是CppUnit，这是C++单元测试工具的鼻祖，免费的开源的单元测试框架。由于已有一众高人写了不少关于CppUnit的很好的文章，老纳就不现丑了，想了解CppUnit的朋友，建议读一下Cpluser 所作的《CppUnit测试框架入门》，网址是：</font><a href="http://blog.csdn.net/cpluser/archive/2004/09/21/111522.aspx" target=_blank><font color=#003366 size=2><u>http://blog.csdn.net/cpluser/archive/2004/09/21/111522.aspx</u></font></a><font size=2>。该文也提供了CppUnit的下载地址。<br>　　然后介绍C++Test，这是Parasoft公司的产品。［C++Test是一个功能强大的自动化C/C++单元级测试工具，可以自动测试任何C/C++函数、类，自动生成测试用例、测试驱动函数或桩函数，在自动化的环境下极其容易快速的将单元级的测试覆盖率达到100%。</font><font size=2>这是华唐公司的网页上的介绍。老纳想写些介绍C++Test的文字，但发现无法超越华唐公司的网页上的介绍，所以也就省点事了，想了解C++Test的朋友，建议访问该公司的网站。华唐公司代理C++Test，想要购买或索取报价、试用版都可以找他们。老纳帮华唐公司做广告，不知道会不会得点什么好处？<br>　　最后介绍Visual Unit，简称VU，这是国产的单元测试工具，据说申请了多项专利，拥有一批创新的技术，不过老纳只关心是不是有用和好用。［自动生成测试代码 快速建立功能测试用例 程序行为一目了然 极高的测试完整性 高效完成白盒覆盖 快速排错 高效调试 详尽的测试报告］。［］内的文字是VU开发商的网页上摘录的，网址是：</font><a href="http://www.unitware.cn/" target=_blank><font color=#003366 size=2><u>http://www.unitware.cn</u></font></a><font size=2>。前面所述测试要求：完成功能测试，完成语句覆盖、条件覆盖、分支覆盖、路径覆盖，用VU可以轻松实现，还有一点值得一提：使用VU还能提高编码的效率，总体来说，在完成单元测试的同时，编码调试的时间还能大幅度缩短。算了，不想再讲了，老纳显摆理论、介绍经验还是有兴趣的，因为可以满足老纳好为人师的虚荣心，但介绍工具就觉得索然无味了，毕竟工具好不好用，合不合用，要试过才知道，还是自己去开发商的网站看吧，可以下载演示版，还有演示课件。</font></span></p>
<p><span style="FONT-SIZE: 12px"><font size=2>转自：</font><a href="http://www.cnblogs.com/tester2test/archive/2006/08/04/467764.html"><font color=#0000ff size=2><u>http://www.cnblogs.com/tester2test/archive/2006/08/04/467764.html</u></font></a></span></p>
</font></span></span>
<img src ="http://www.cppblog.com/shuiyuan2004/aggbug/30851.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/shuiyuan2004/" target="_blank">不倦</a> 2007-08-26 14:33 <a href="http://www.cppblog.com/shuiyuan2004/archive/2007/08/26/30851.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>