您的位置:首页 > 编程语言 > C语言/C++

学习笔记:解读CppUnit源码4

2009-11-10 09:58 253 查看
可能大家都有这样的疑问:测试用例运行过程中,要是出现错误,异常甚至崩溃的情况下是在哪里捕捉到的呢?那么我们就从这里入手慢慢的来了解TestResult的各个机能。

请大家先回忆一下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是最最核心的部分 其作用是对执行函数(实际上是函数对象)的错误信息(包括断言和异常等)进行捕获,从而实现对测试结果的统计
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: