Google test初步分析.

现在常用的C++单元测试框架有 CppUnit,CxxTest,boost::test和google test。不像java/C#的测试框架,由于C++不支持reflection,所以,必须要做一些额外的工作,让框架知道相关内容的存在。CppUnit的做法是用宏进行注册。这种做法要求我们每添加一个测试,就要考虑用相应的宏进行注册,这种做法很繁琐,最大的问题在于由于疏忽而遗漏,这种靠人工保证的东西不可靠。在这点上,CxxTest 做得要好一些,有一个专门的脚本做这件事。通过这个脚本扫描这个自己编写的文件,生成一些新的文件,完成这个工作。从代码的表现力和可靠度来说,要好得多。唯一的问题是引入了一个脚本,而且这个脚本一般是由某些动态语言写成的(目前的CxxTest有Perl和Python的脚本),从而引入了对这种语言的依赖。而boost::test和google test 则是通过一些宏机制,来实现用户只需写一次就可以自动完成注册。
我大概分析了一下gtest的实现机制。由于里面用了很多宏,比较费解,因此首先用cl /P /C命令将之展开,我的测试程序如下
#include <gtest/gtest.h>
TEST(FactorialTest, Negative) 
{
  EXPECT_EQ(
11);
}

宏展开完后主要部分如下
class FactorialTest_Negative_Test : public ::testing::Test
{
    
public: FactorialTest_Negative_Test() {}
    
privatevirtual void TestBody();
    
static ::testing::TestInfo* const test_info_;
    FactorialTest_Negative_Test(
const FactorialTest_Negative_Test &);
    
void operator=(const FactorialTest_Negative_Test &);
}
;

//init test_info_
::testing::TestInfo* const FactorialTest_Negative_Test ::test_info_ = ::testing::internal::MakeAndRegisterTestInfo( "FactorialTest""Negative""""", (::testing::internal::GetTestTypeId()), ::testing::Test::SetUpTestCase, ::testing::Test::TearDownTestCase, new ::testing::internal::TestFactoryImpl< FactorialTest_Negative_Test>);

void FactorialTest_Negative_Test::TestBody() {
  
// This test is named "Negative", and belongs to the "FactorialTest"
  
// test case.
  switch (0case 0if (const ::testing::AssertionResult gtest_ar = (::testing::internal:: EqHelper<(sizeof(::testing::internal::IsNullLiteralHelper(1)) == 1)>::Compare("1""Factorial(-5)"1, Factorial(-5)))) ; else ::testing::internal::AssertHelper(::testing::TPRT_NONFATAL_FAILURE, "sample1_unittest.cc"82, gtest_ar.failure_message()) = ::testing::Message();

}


可以看出,gtest是利用static变量的初始化来实现函数注册的,主要函数为MakeAndRegisterTestInfo(),该函数定义在src\gtest.cc里面
TestInfo* MakeAndRegisterTestInfo(
    
const char* test_case_name, const char* name,
    
const char* test_case_comment, const char* comment,
    TypeId fixture_class_id,
    SetUpTestCaseFunc set_up_tc,
    TearDownTestCaseFunc tear_down_tc,
    TestFactoryBase
* factory)
{
  TestInfo
* const test_info =
      
new TestInfo(test_case_name, name, test_case_comment, comment,
                   fixture_class_id, factory);
  GetUnitTestImpl()
->AddTestInfo(set_up_tc, tear_down_tc, test_info);    //关键是这一行
  return test_info;
}

果然不出所料,GetUnitTestImpl()->AddTestInfo(xx)这一句就是做注册,下面我仿照gtest自己写了一个简单的测试框架
#include <string>
#include 
<vector>
#include 
<iostream>
using namespace std;

class Test
{
    
public:
    
virtual bool testBody() = 0;
}
;

class TestInfo
{
    
public:
        TestInfo(
const char *fun,const char *file,int line, Test * pTest):
        szFunction_(fun),szFile_(file),line_(line),test_(pTest)
        
{
        }

        
        
bool RunTest()
        
{
            
return test_->testBody();
        }

        
        
string szFunction_;
        
string szFile_;
        
int     line_;
    
private:
        
        Test    
*test_;
}
;

typedef    vector 
< TestInfo *> TestInfosType;
TestInfosType testInfos;

TestInfo 
* MakeAndRegisterTestInfo(const char *fun,const char *file,int line, Test * pTest)
{
    
//cout << "MakeAndRegisterTestInfo..\n";
    TestInfo * pInfo = new TestInfo(fun, file,line,pTest);
    testInfos.push_back(pInfo);
    
return pInfo;
}


#define TEST(testName)    \
class TEST_##testName: public Test    \
{                    \
    
public:    \
    
virtual bool testBody();    \
    
static TestInfo * test_info_;    \
}
;    \
TestInfo 
* TEST_##testName::test_info_ = MakeAndRegisterTestInfo(#testName,__FILE__,__LINE__,new TEST_##testName);    \
bool TEST_##testName::testBody()    


#define TEST_ASSERT(x)  {if(!(x)) {cout << "AssertFail:" << #x << " File:" << test_info_->szFile_.c_str() << " Line:" << test_info_->line_ << " Function:" << test_info_->szFunction_.c_str() << endl;return false;}else cout<<"."<<endl;}

#define RUN_ALL_TEST()    \
for(TestInfosType::iterator it = testInfos.begin();    \
        it 
!= testInfos.end(); ++it)    \
{    \
    TestInfo 
* pInfo = *it;    \
    pInfo
->RunTest();    \
}
    \


使用该测试框架的方法如下

TEST(test1)
{
    TEST_ASSERT(
2==2);
    TEST_ASSERT(
2==1);
}


TEST(test2)
{
    TEST_ASSERT(
3==3);
    TEST_ASSERT(
2==3);
}



void main()
{
    cout 
<< "start test..\n";
    RUN_ALL_TEST();
}


输出结果:

I:\VC2008\gtest-1.2.1>mySimulate.exe
start test..
.
AssertFail:2==1 File:mySimulate.cpp Line:66 Function:test1
.
AssertFail:2==3 File:mySimulate.cpp Line:72 Function:test2

尚有点问题就是,现实的错误行号其实为函数第一行的行号。

Powered by Jekyll and Theme by solid

本站总访问量