学习笔记:解读CppUnit源码4
2009-11-10 09:58
253 查看
可能大家都有这样的疑问:测试用例运行过程中,要是出现错误,异常甚至崩溃的情况下是在哪里捕捉到的呢?那么我们就从这里入手慢慢的来了解TestResult的各个机能。
请大家先回忆一下TestCase::Run方法。
在这个方法里面,其实调用的是TestResult的protect()方法,看到这里也不难推测TestResult::protect()里面是真正执行并捕捉错误的地方。
TestResult是一个记录所有测试结果的类。而程序运行的防错机制:try-catch放在了Protector类的子类中。先看看Protector类的源码。
Protector.h
这里的reportError(),reportFailure()等方法,是在子类的错误捕捉到以后为了输出而准备的。具体输出什么如何输出大家看代码就可以了。而protect()方法是纯虚函数,真正需要保护的东西是在它的子类实现。这样做的目的很明确,错误的输出是固定的,所以放在父类,而如何保护测试用例的执行(也就是try-catch)放在子类也是方便修改和便于扩展。
DefaultProtector.h
以上是DefaultProtector的代码,它就是继承了Protector类,并实现了Protector类的纯虚函数protect()。这个类才是真正执行测试用例保护测试用例的地方。
接下去的话题将围绕protect()而展开。知道protect()的作用后,那么它如何调用,什么时候调用呢?还有两个参数分别代表什么意思。
ProtectorContext.h
这个类将Test,TestResult这些对象聚集在了一起,看过DefaultProtector代码就知道,聚集在一起是因为当发生异常的时候需要这些信息来输出。
Protector::protect()的另外一个参数是Functor,也就是函数对象。在介绍TestCase的时候,就已经提到过Functor了,而这里将在一次说明它
定义函数对象最主要是为了实现命令模式,初始化函数对象的时候把必要的东西通过构造函数预先传入,用重载操作符实现回调。
在ProtectorChain类里面,定义了一个inner class叫做ProtectFunctor,ProtectFunctor就是继承自Functor。
ProtectFunctor继承自Functor,而继承自ProtectFunctor里面又有一个成员是Functor,哈哈。一看就知道这个是装饰者模式对这个函数对象内容的扩展。
上面代码第15行:m_protector->protect( m_functor, m_context );
m_protector对象的类有个体子类和组合子类之分,所以有了多态的灵活特性。具体参见下面的ProtectorChain。
m_functor函数对象就是实现装饰者模式的根本。
最后也是这个设计中最美妙的地方:ProtectorChain.h
上面代码第三行:ProtectorChain是Protector的子类,再看第39行:ProtectorChain里面存放着Protector的一个双端链表。如果DefaultProtector子类是叶子节点的话,ProtectorChain应该就是组合。这些应该也是组合模式的一种不完全应用把。
代码第35行:这个inner class的类(函数对象)代码已经在先前给出了。
最后的最后也就是ProtectorChain.cpp也是最核心的代码块
关于错误异常这方面的类结构就如上所示,同时将好几个设计模式应用在了里面,至于具体如何输出错误异常等信息,将在后面继续分析之。
总结上面这些代码:protect是最最核心的部分 其作用是对执行函数(实际上是函数对象)的错误信息(包括断言和异常等)进行捕获,从而实现对测试结果的统计。
请大家先回忆一下TestCase::Run方法。
virtual void run(MyTestResult *result) { //result->protect中会捕捉异常,并记录信息 result->startTest(this); //对于每个测试方法,都先执行其所属TestFixture的setUp方法 if ( result->protect( TestCaseMethodFunctor( this, &TestCase::setUp ), this, "setUp() failed" ) ) { //执行测试方法,TestCase::runTest执行真正的测试函数,参见测试对象构造部分 result->protect( TestCaseMethodFunctor( this, &TestCase::runTest ), this ); } //对于每个测试方法,都后执行其所属TestFixture的tearDown方法 result->protect( TestCaseMethodFunctor( this, &TestCase::tearDown ), this, "tearDown() failed" ); //关于TestResult::protect下次再详细解说 result->endTest( this ); };
在这个方法里面,其实调用的是TestResult的protect()方法,看到这里也不难推测TestResult::protect()里面是真正执行并捕捉错误的地方。
TestResult是一个记录所有测试结果的类。而程序运行的防错机制:try-catch放在了Protector类的子类中。先看看Protector类的源码。
Protector.h
class CPPUNIT_API MyProtector { public: virtual ~MyProtector(){}; virtual bool protect( const MyFunctor &functor, const MyProtectorContext &context ) =0; protected: //增加一条错误信息到ProtectorContext(TestResult) void reportError( const MyProtectorContext &context, const MyException &error ) const { std::auto_ptr<MyException> actualError( error.clone() ); actualError->setMessage( actualMessage( actualError->message(), context ) ); //调用TestResult的addError方法 context.m_result->addError( context.m_test, actualError.release() ); }; void reportError( const MyProtectorContext &context, const MyMessage &message, const MySourceLine &sourceLine = MySourceLine() ) const { reportError( context, MyException( message, sourceLine ) ); }; //增加一条失败信息到ProtectorContext(TestResult) void reportFailure( const MyProtectorContext &context, const MyException &failure ) const { std::auto_ptr<MyException> actualFailure( failure.clone() ); actualFailure->setMessage( actualMessage( actualFailure->message(), context ) ); //调用TestResult的addFailure方法 context.m_result->addFailure( context.m_test, actualFailure.release() ); }; //如果描述的字符串为空,把参数的字符串返回,否则插入信息到Message并返回 MyMessage actualMessage( const MyMessage &message, const MyProtectorContext &context ) const { MyMessage theActualMessage; if ( context.m_shortDescription.empty() ) theActualMessage = message; else { theActualMessage = MyMessage( context.m_shortDescription, message.shortDescription() ); theActualMessage.addDetail( message ); } return theActualMessage; }; };
这里的reportError(),reportFailure()等方法,是在子类的错误捕捉到以后为了输出而准备的。具体输出什么如何输出大家看代码就可以了。而protect()方法是纯虚函数,真正需要保护的东西是在它的子类实现。这样做的目的很明确,错误的输出是固定的,所以放在父类,而如何保护测试用例的执行(也就是try-catch)放在子类也是方便修改和便于扩展。
DefaultProtector.h
class MyDefaultProtector : public MyProtector { public: bool protect( const MyFunctor &functor, const MyProtectorContext &context ) { try { return functor(); } catch ( MyException &failure ) { //CPPUINT预期的异常,一般为用户编写的测试函数中的断言错误 reportFailure( context, failure ); } catch ( std::exception &e ) { //CPPUINT非预期的异常,是C++抛出的异常 std::string shortDescription( "uncaught exception of type " ); shortDescription += "std::exception (or derived)."; MyMessage message( shortDescription, e.what() ); reportError( context, message ); } catch ( ... ) { //CPPUINT非预期的异常,剩下所有的异常 reportError( context, MyMessage( "uncaught exception of unknown type") ); } return false; }; };
以上是DefaultProtector的代码,它就是继承了Protector类,并实现了Protector类的纯虚函数protect()。这个类才是真正执行测试用例保护测试用例的地方。
接下去的话题将围绕protect()而展开。知道protect()的作用后,那么它如何调用,什么时候调用呢?还有两个参数分别代表什么意思。
ProtectorContext.h
//ProtectorContext是封装了Test、TestResult和描述信息的对象 class CPPUNIT_API MyProtectorContext { public: MyProtectorContext( MyTest *test, MyTestResult *result, const std::string &shortDescription ) : m_test( test ) , m_result( result ) , m_shortDescription( shortDescription ) { }; MyTest *m_test; MyTestResult *m_result; std::string m_shortDescription; };
这个类将Test,TestResult这些对象聚集在了一起,看过DefaultProtector代码就知道,聚集在一起是因为当发生异常的时候需要这些信息来输出。
Protector::protect()的另外一个参数是Functor,也就是函数对象。在介绍TestCase的时候,就已经提到过Functor了,而这里将在一次说明它
//抽象的函数对象 class CPPUNIT_API MyFunctor { public: virtual ~MyFunctor(){}; virtual bool operator()() const =0; };
定义函数对象最主要是为了实现命令模式,初始化函数对象的时候把必要的东西通过构造函数预先传入,用重载操作符实现回调。
在ProtectorChain类里面,定义了一个inner class叫做ProtectFunctor,ProtectFunctor就是继承自Functor。
class MyProtectorChain::ProtectFunctor : public MyFunctor { public: ProtectFunctor( MyProtector *protector, const MyFunctor &functor, const MyProtectorContext &context ) : m_protector( protector ) , m_functor( functor ) , m_context( context ) { } bool operator()() const { return m_protector->protect( m_functor, m_context ); } private: MyProtector *m_protector; const MyFunctor &m_functor; const MyProtectorContext &m_context; };
ProtectFunctor继承自Functor,而继承自ProtectFunctor里面又有一个成员是Functor,哈哈。一看就知道这个是装饰者模式对这个函数对象内容的扩展。
上面代码第15行:m_protector->protect( m_functor, m_context );
m_protector对象的类有个体子类和组合子类之分,所以有了多态的灵活特性。具体参见下面的ProtectorChain。
m_functor函数对象就是实现装饰者模式的根本。
最后也是这个设计中最美妙的地方:ProtectorChain.h
#define CppUnitDeque std::deque class CPPUNIT_API MyProtectorChain : public MyProtector { public: ~MyProtectorChain() { while ( count() > 0 ) pop(); }; //加入一个保护者 void push( MyProtector *protector ) { m_protectors.push_back( protector ); }; //弹出一个保护者 void pop() { delete m_protectors.back(); m_protectors.pop_back(); }; //保护者的总数 int count() const { return m_protectors.size(); }; //保护 bool protect( const MyFunctor &functor, const MyProtectorContext &context ); private: //此类又是一个函数对象,同样使用了装饰者模式,里面进一步把用户编写的测试函数 //以及和Protector、ProtectorContext给封装起来,这里为后面的Protector递归包裹 //奠定了基础 class ProtectFunctor; private: //保护者的链表 typedef CppUnitDeque<MyProtector *> Protectors; Protectors m_protectors; //函数对象的链表 typedef CppUnitDeque<MyFunctor *> Functors; };
上面代码第三行:ProtectorChain是Protector的子类,再看第39行:ProtectorChain里面存放着Protector的一个双端链表。如果DefaultProtector子类是叶子节点的话,ProtectorChain应该就是组合。这些应该也是组合模式的一种不完全应用把。
代码第35行:这个inner class的类(函数对象)代码已经在先前给出了。
最后的最后也就是ProtectorChain.cpp也是最核心的代码块
MyProtectorChain::protect( const MyFunctor &functor, const MyProtectorContext &context ) { if ( m_protectors.empty() ) return functor(); Functors functors; //这里把登记的所有Protector进行递归嵌套,注意Protector的嵌套顺序 //跟Protector的登记顺序是相反的,也就是先登记的Protector会先执行 for ( int index = m_protectors.size()-1; index >= 0; --index ) { const MyFunctor &protectedFunctor = functors.empty() ? functor : *functors.back(); functors.push_back( new ProtectFunctor( m_protectors[index], protectedFunctor, context ) ); } //取出最顶层的一个ProtectorFunctor进行最终的函数执行 const MyFunctor &outermostFunctor = *functors.back(); bool succeed = outermostFunctor(); for ( unsigned int deletingIndex = 0; deletingIndex < m_protectors.size(); ++deletingIndex ) delete functors[deletingIndex]; return succeed; }
关于错误异常这方面的类结构就如上所示,同时将好几个设计模式应用在了里面,至于具体如何输出错误异常等信息,将在后面继续分析之。
总结上面这些代码:protect是最最核心的部分 其作用是对执行函数(实际上是函数对象)的错误信息(包括断言和异常等)进行捕获,从而实现对测试结果的统计。
相关文章推荐
- uboot.lds解读
- 《Java程序员面试宝典》学习笔记(数据结构部分)
- 带有const修饰的指针解读
- VC文件扩展名解读大全
- Day3 : Auto layout 和 JVFloatLabeledTextfield框架 学习笔记
- String源码分析,解读面试题
- Linux内核解读入门(转CSDN)
- 《机器学习实战》学习笔记3
- 解读Zend框架(二) 环境配置
- 【Facebook的UI开发框架React入门之四】index.ios.js解读(iOS平台)-goodmao
- 《effective c++》学习笔记(一)
- 解读set_gpio_ctrl(GPIO_MODE_OUT | GPIO_H6)
- (原创)《Android编程权威指南》学习笔记01-- Android应用初体验--003
- 《effective c++》学习笔记(一)
- 《xUnit Test Patterns》学习笔记4 - Principles of Test Automation
- 解读郭神LitePal源码-表的创建
- 《effective c++》学习笔记(一)
- 转:无线通信原理通俗解读
- 《Java从入门到精通》第五章学习笔记