TestResult作为Data Modal,TestLisener作为subscriber。程序运行时需要向TestResult注册观察者,如:
// 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 );
以后不管是一个TestSuite要开始测试,还是一个TestCase要开始运行,TestResult都会通知所有的观察者,这种通知很简单,仅仅是循环遍历所有观察者,调用其相关的函数而已。
比如TestSuite要开始测试了。
void
TestResult::startSuite( Test *test )
{
ExclusiveZone zone( m_syncObject );
for ( TestListeners::iterator it = m_listeners.begin();
it != m_listeners.end();
++it )
(*it)->startSuite( test );
}
查看cppunit观察者的代码发现,很多函数它都仅有一个空的函数体而已,但是这里留有了扩展性,如果以后想在这些地方添加些代码是非常方便的。恩,我想这里的耦合还是比较大的,TestResult必须知道TestListener都有哪些方法要调用,如果能将数据模型和观察者之前的通信所要交流的信息封装成一种消息就可以大大减少这种耦合,观察者实现一个Update接口,该接口接受消息类的基类作为参数,这样TestResult就无须过多了解TestListener的内部细节了,构造好相应的消息之后,就调用向其注册的观察者的Update方法。这样做的缺点就是要引入很多消息类。
观察者有一个比较核心的函数就是addFailure。
TestResult的addFailure代码如下:
void
TestResult::addFailure( Test *test, Exception *e )
{
TestFailure failure( test, e, false );
addFailure( failure );
}
void
TestResult::addFailure( const TestFailure &failure )
{
ExclusiveZone zone( m_syncObject );
for ( TestListeners::iterator it = m_listeners.begin();
it != m_listeners.end();
++it )
(*it)->addFailure( failure );
}
可以看出,如果一个测试失败了,它首先构造一个TestFailure对象,然后将依次调用所有的观察者的addFailure接口,TestResultCollector这个从TestSuccessListener派生的子类的addFailure实现如下:
void
TestResultCollector::addFailure( const TestFailure &failure )
{
TestSuccessListener::addFailure( failure );
ExclusiveZone zone( m_syncObject );
if ( failure.isError() )
++m_testErrors;
m_failures.push_back( failure.clone() );
}
它将失败都保存到一个容器里面,使用Outputter对象变可以将这些错误信息全部输出来了。
// 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 );
以后不管是一个TestSuite要开始测试,还是一个TestCase要开始运行,TestResult都会通知所有的观察者,这种通知很简单,仅仅是循环遍历所有观察者,调用其相关的函数而已。
比如TestSuite要开始测试了。
void
TestResult::startSuite( Test *test )
{
ExclusiveZone zone( m_syncObject );
for ( TestListeners::iterator it = m_listeners.begin();
it != m_listeners.end();
++it )
(*it)->startSuite( test );
}
查看cppunit观察者的代码发现,很多函数它都仅有一个空的函数体而已,但是这里留有了扩展性,如果以后想在这些地方添加些代码是非常方便的。恩,我想这里的耦合还是比较大的,TestResult必须知道TestListener都有哪些方法要调用,如果能将数据模型和观察者之前的通信所要交流的信息封装成一种消息就可以大大减少这种耦合,观察者实现一个Update接口,该接口接受消息类的基类作为参数,这样TestResult就无须过多了解TestListener的内部细节了,构造好相应的消息之后,就调用向其注册的观察者的Update方法。这样做的缺点就是要引入很多消息类。
观察者有一个比较核心的函数就是addFailure。
TestResult的addFailure代码如下:
void
TestResult::addFailure( Test *test, Exception *e )
{
TestFailure failure( test, e, false );
addFailure( failure );
}
void
TestResult::addFailure( const TestFailure &failure )
{
ExclusiveZone zone( m_syncObject );
for ( TestListeners::iterator it = m_listeners.begin();
it != m_listeners.end();
++it )
(*it)->addFailure( failure );
}
可以看出,如果一个测试失败了,它首先构造一个TestFailure对象,然后将依次调用所有的观察者的addFailure接口,TestResultCollector这个从TestSuccessListener派生的子类的addFailure实现如下:
void
TestResultCollector::addFailure( const TestFailure &failure )
{
TestSuccessListener::addFailure( failure );
ExclusiveZone zone( m_syncObject );
if ( failure.isError() )
++m_testErrors;
m_failures.push_back( failure.clone() );
}
它将失败都保存到一个容器里面,使用Outputter对象变可以将这些错误信息全部输出来了。