界面部分(TextUI)


这一部分主要提供了一个文本界面的测试运行环境(即以字符流方式输出到标准输出设备)。该测试环境在CppUnit中被称为test runner,对应的类是TestRunner。TestRunner可以运行所有测试,或者是其中的一个。下面的代码演示了如何使用TestRunner:

CppUnit::TextUi::TestRunner runner;
runner.addTest( ExampleTestCase::suite() );
runner.run( "", true );    // 空字串""代表运行所有的测试

在测试执行期间,TestRunner除了能输出最后的统计结果外,还可以打印输出跟踪信息。其中,跟踪信息的输出使用了TextTestProgressListener(见listener部分),统计结果的输出则使用了TextOutputter(见outputter部分)。当然,这些都是可选的。你可以在构造TestRunner期间或者随后通过调用setOutputter函数来指定其他类型的outputter。你也可以通过在eventManager()中注册其他TestListener,来定制跟踪信息。且看下面的示例:

CppUnit::TextUi::TestRunner runner;
runner.addTest( ExampleTestCase::suite() );

// 用CompilerOutputter代替TextOutputter
runner.setOutputter( CppUnit::CompilerOutputter::defaultOutputter(
    &runner.result(), std::cerr ) );
// 添加自定义的MyCustomProgressTestListener
MyCustomProgressTestListener progress;
runner.eventManager().addListener( &progress );

runner.run( "", true );    // 空字串""代表运行所有的测试

最后,TestRunner管理着其下所有测试对象的生命周期。

[TestRunner]

相关文件:TestRunner.h,TestRunner.cpp,TextTestRunner.h

TestRunner中定义了4个protected属性的成员变量:

TestSuite *m_suite;                     // 对应待运行的测试
TestResultCollector *m_result;          // 搜集测试结果(见output部分)
TestResult *m_eventManager;             // 收集测试过程中的相关信息
Outputter *m_outputter;                 // 输出测试统计结果

对于这几个成员变量的作用,注释中及前面部分已有提及。在这里,大家对TestResult和TestResultCollector的功能可能容易混淆。对于它们的区别以及TestResultCollector的“身世”,请见output部分。

TestRunner的ctor对这几个成员变量进行了初始化,若外部传入的outputter为空,则缺省创建TextOutputter对象赋给m_outputter,另外还调用了m_eventManager的addListener方法,将m_result作为一个兼听者加入其中。

TestRunner::TestRunner( Outputter *outputter ) 
    : m_outputter( outputter )
    , m_suite( new TestSuite( "All Tests" ) )
    , m_result( new TestResultCollector() )
    , m_eventManager( new TestResult() )
{
  if ( !m_outputter )
    m_outputter = new TextOutputter( m_result, std::cout );
  m_eventManager->addListener( m_result );
}

dtor则负责回收在ctor中所创建的资源:

TestRunner::~TestRunner()
{
  delete m_eventManager;
  delete m_outputter;
  delete m_result;
  delete m_suite;
}

正如前面所说,你可以在构造TestRunner之后,通过调用setOutputter函数来指定其他类型的outputter:

void TestRunner::setOutputter( Outputter *outputter )
{
  delete m_outputter;
  m_outputter = outputter;
}

TestRunner中的主要接口就是run方法,通过调用该函数才能运行测试:

bool TestRunner::run( std::string testName, 
        bool doWait, 
        bool doPrintResult, 
        bool doPrintProgress )
{
    runTestByName( testName, doPrintProgress );
    printResult( doPrintResult );
    wait( doWait );
    return m_result->wasSuccessful();
}

其中,testName是测试用例的名称,若为空则运行所有测试,否则运行指定测试;doWait为true时,表示在run函数返回之前,用户必须按一下RETURN键才能结束;doPrintResult为true时,测试结果将被输出到标准输出设备(前提是使用TextOutputter),否则不产生任何输出;doPrintProgress为true时,测试过程将被输出到标准输出设备(前提是使用TextTestProgressListener),否则不产生任何输出。

这里调用了runTestByName,请看源码及注释:

bool TestRunner::runTestByName( std::string testName,
    bool doPrintProgress )
{
  if ( testName.empty() )                       // 若testName为空则运行所有测试
    return runTest( m_suite, doPrintProgress );

  Test *test = findTestByName( testName );      // 否则查找指定测试
  if ( test != NULL )
    return runTest( test, doPrintProgress );    // 若成功找到则运行之

  std::cout << "Test " << testName << " not found." << std::endl;
  return false;
}

在runTestByName中,根据测试名称查找指定测试的任务落实到了findTestByName肩上:

Test *TestRunner::findTestByName( std::string name ) const
{
  for ( std::vector<Test *>::const_iterator it = m_suite->getTests().begin();
    it != m_suite->getTests().end();
    ++it )
  {
    Test *test = *it;
    if ( test->getName() == name )
      return test;
  }
  return NULL;
}

该函数以const_iterator遍历m_suite中的所有测试,寻找名字相符的测试。不过,从代码中可以看出,这里无法支持多层嵌套结构的测试集,这也算是一点遗憾吧。

找到指定测试之后,就可以将运行测试的任务转交给runTest方法了:

bool TestRunner::runTest( Test *test, bool doPrintProgress)
{
  TextTestProgressListener progress;
  if ( doPrintProgress )                // 若doPrintProgress为true则显示测试过程
    m_eventManager->addListener( &progress );

  test->run( m_eventManager );          // 此处才真正运行测试,m_eventManager就是TestResult

  if ( doPrintProgress )                // 若doPrintProgress为true则移除先前加入的progress
    m_eventManager->removeListener( &progress );
  return m_result->wasSuccessful();     // 返回测试结果成功与否
}

测试结束后,就可以通过调用printResult输出测试结果了:

void TestRunner::printResult( bool doPrintResult )
{
  std::cout << std::endl;
  if ( doPrintResult )
    m_outputter->write();
}

另外,TestRunner还提供了一个addTest方法,用以添加测试,其内部只是简单的调用了一下m_suite的addTest方法,相当简单:

void TestRunner::addTest( Test *test )
{
  if ( test != NULL )
    m_suite->addTest( test );
}

当然还少不了几个成员变量的getter方法:

TestResultCollector &TestRunner::result() const
{
  return *m_result;
}

TestResult &TestRunner::eventManager() const
{
  return *m_eventManager;
}

另一个相关的头文件TextTestRunner.h中,仅仅简单地做了一个typedef定义:

typedef CppUnit::TextUi::TestRunner TextTestRunner;

这就是TestRunner的大致内容。

返回目录