对新框架的学习,一种比较自然的方法就是直接使用,看到运行结果,跟踪运行过程,了解框架的运行流程。就像学习各种编程语言一样,每本书上来都是“Hello world“,那么我的学习过程也是以直接使用框架开始。
1、编译CppUnit框架,获得相关的导出库文件和运行时库文件
2、设置相关的vs工程
3、建立自己的测试框架和测试用例
4、运行测试用例
5、分析用例的执行过程,获得感性上的认识
源文件代码如下:
ExampleTestCase.h
#ifndef CPP_UNIT_EXAMPLETESTCASE_H
#define CPP_UNIT_EXAMPLETESTCASE_H
#include
/*
* A test case that is designed to produce
* example errors and failures
*
*/
class ExampleTestCase : public CPPUNIT_NS::TestFixture
{
CPPUNIT_TEST_SUITE( ExampleTestCase );
CPPUNIT_TEST( testEquals );
CPPUNIT_TEST_SUITE_END();
protected:
double m_value1;
double m_value2;
public:
void setUp();
protected:
void testEquals(); // 测试用例
};
#endif
ExampleTestCase.cpp
#include "ExampleTestCase.h"
CPPUNIT_TEST_SUITE_REGISTRATION( ExampleTestCase );
void ExampleTestCase::testEquals()
{
long* l1 = new long(12);
long* l2 = new long(12);
CPPUNIT_ASSERT_EQUAL( 12, 12 );
CPPUNIT_ASSERT_EQUAL( 12L, 12L );
CPPUNIT_ASSERT_EQUAL( *l1, *l2 );
delete l1;
delete l2;
CPPUNIT_ASSERT( 12L == 12L );
CPPUNIT_ASSERT_EQUAL( 12, 13 );
CPPUNIT_ASSERT_DOUBLES_EQUAL( 12.0, 11.99, 0.5 );
}
main.cpp
#include
#include
#include
#include
#include
#include
int main( int argc, char* argv[] )
{
// Create the event manager and test controller,驱动整个测试框架的运行
CPPUNIT_NS::TestResult controller;
// Add a listener that colllects test result,收集相关测试用例的测试结果
CPPUNIT_NS::TestResultCollector result;
controller.addListener( &result );
// Add a listener that print dots as test run.同步显示测试用例的测试结果
CPPUNIT_NS::BriefTestProgressListener progress;
controller.addListener( &progress );
// Add the top suite to the test runner,添加测试suite,在controller的控制下进行测试用例的运行
CPPUNIT_NS::TestRunner runner;
runner.addTest( CPPUNIT_NS::TestFactoryRegistry::getRegistry().makeTest() );
runner.run( controller );
// Print test in a compiler compatible format.测试结果的输出
CPPUNIT_NS::CompilerOutputter outputter( &result, std::cerr );
outputter.write();
return result.wasSuccessful() ? 0 : 1;
}
运行结果:
在CppUnit中提供了丰富的测试异常的捕捉的宏定义,可能大家看了相关的源码后就很奇怪,为什么在main文件中没有测试用例集合的创建,而整个测试用例工程却可以正常的运行,那么整个关键地方就是在ExampleTestCase 的测试集合中的宏定义和相关的实现文件中的CPPUNIT_TEST_SUITE_REGISTRATION( ExampleTestCase )
这个宏提供了如何把与测试用例集合相关的注册对象自动注册到全局的单件工厂中,这个单件工厂是一个static的局部变量,自动注册类是一个全局的对象,在C++中static对象在主程序main还没有运行的时候就已经创建了,因此在上面中,当main函数开始运行的时候CppUnit框架早就已经创建了全局的测试工厂对象和ExampleTestCase的自动注册工厂对象,然后在main运行过程中进行单个测试用例的创建并且添加到整个TestCase的用例树中【CPPUNIT_NS::TestFactoryRegistry::getRegistry().makeTest()】的作用即在于此。
这些分析过程在后面都可以看到,自己跟踪过程过程中画了一下每个对象的生命周期图,仅仅只是为了分析,没有参考UML的语法规则,不对的地方请不要拍砖了。
跟踪了整个函数调用堆栈的过程基本上是这样了:调用测试用例setup的过程
通过上面部分代码的跟踪可以看到:首先是TestRunner启动测试用例的运行(个人加它加载器吧),然后由TestResult(控制器)控制整个测试用例的运行,默认有一个测试用例的树的根节点(WrappingSuite),然后在递归的运行自己的孩子测试用例(TestComposite),然后再递归,最后运行单个测试用例(TestCase),测试用例把包装在默认的保护环境(DefaultProtector)中,最后通过一个类封装对象(把ExampleTestCase中的函数封装成了成员函数指针)调用ExampleTestCase的setUp,在后面运行测试用例,收集结果,输出。
整个运行情况基本上如上所示,涉及到的抽象对象有TestResult(管理器)、TestResultCollector(收集器)、TestRunner(启动器)、CompilerOutputter(输出器)
上面的叫法都是我个人的叫法,不必深究。