• 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

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

  • 相关阅读:
    python CreateUniqueName()创建唯一的名字
    node 创建静态服务器并自动打开浏览器
    基于jQuery 的插件开发
    Fetch
    纯css 来实现下拉菜单
    javascript模板引擎之
    jquery jsonp 跨域
    数据库增删改查
    Promise
    Vue.js
  • 原文地址:https://www.cnblogs.com/cutepig/p/1412640.html
Copyright © 2020-2023  润新知