一   使用

 

先说一下如何使用这个framework。其基本用法和java的junit差不多,只是没那么强大而已。看一下下面所示的这个IntWraper 类:

 

 

#ifndef _INTWRAPER_H
#define _INTWRAPER_H
class IntWraper
{
public:
	IntWraper(int n):
	  m_nInteger(n){}

	  void Value(int newValue){m_nInteger = newValue;}
	  int Value( void )const{return m_nInteger;}
	  IntWraper& operator += (IntWraper& rhs)
	  { 
		  m_nInteger += rhs.Value();
		  return *this;
	  }
private:
	int m_nInteger;
};
#endif //_INTWRAPER_H

 

 现在要给它写一个unittest ,你只要做如下工作就可以了:

      1.  包含头文件: #include "FAssert.h"
      2.  定义一个测试类,在这个类中使用下列宏来申明test case:

                DECLARE_AS_TESTER                //申明一个类为tester,需要放在开头[必有]

                DECLARE_SETUP                        //申明一个setup方法[可选]

                DECLARE_TEARDOWN                //申明一个teardown方法用于清理工作[可选]

                DECLARE_TEST(testName)          //申明一个test[至少有一个,否则没意义]

         3.  实现定义的setup,teardown,和test方法;

         4.  调用宏CALL_TEST注册这个unit test:

           5.  在主程序中定义一个TestMain,然后调用它的run方法:

                   using namespace FTest;

                   TestMain myTest(std::cout);

                   myTest.run();

          对IntWraper的完整的test 代码如下:

           

          代码
          // SampleTest.cpp : Defines the entry point for the console application.
          //

          #include
          "IntWraper.h"
          #include
          "FAssert.h"
          #include
          "IntWraper.h"
          class TestInt
          {
          DECLARE_AS_TESTER(TestInt)

          DECLARE_SETUP

          DECLARE_TEARDOWN

          DECLARE_TEST(testValue)

          DECLARE_TEST(testAdd)
          private:
          IntWraper
          *m_pInt;
          };

          void TestInt::setup( void )
          {
          m_pInt
          = new IntWraper(0);
          }

          void TestInt::tearDwon( void )
          {
          delete m_pInt;
          }

          void TestInt::testValue( void )
          {
          ASSERTEQUAL(
          0,m_pInt->Value());
          }
          void TestInt::testAdd( void )
          {
          IntWraper other(
          4);
          *m_pInt += other;
          ASSERTEQUAL(
          3 , m_pInt->Value());
          }

          CALL_TEST(TestInt)

          #include
          <iostream>
          int main(int argc, char* argv[])
          {
          using namespace FTest;
          TestMain myTest(std::cout);
          myTest.run();
          return 0;
          }

           

          运行后你会得到输出结果:

          一个简单地C++ Unit Test framework

          从这个结果你可以看到一共运行了多少个test,成功多少,失败多少,如果失败了,还会给出失败的具体位置.

           

          二  框架

              下面这个结构图是从xunit的结构图演变而来的,只不过在这里的testcase是由一系列的IFuncObject组成。对上面给出的例子来说,setup,teardown,testValue和testAdd都对应一个IFuncObject.这些IFuncObject只是对保存了setup,teardown,和test函数这些成员函数指针而已。在TestCase的run方法被调用的时候,按照下列顺序调用这些IFuncObject的Do方法:

                  setup

                  test1

                  ...

                  teardown

          一个简单地C++ Unit Test framework

           

             最后在内存中会形成如下图所示的一棵树。

          一个简单地C++ Unit Test framework

               这棵树有点特殊,因为它只有三层,最顶层的是一个suite,第二层是一些testcase,它们与程序员写的tester是一一对应的。最底层的是一些IFuncObject对象,它们对应与tester中的setup,teardown和具体的测试函数。由于对IFuncObject应用了NullObject模式,所以无论是否定义setup和teardown,每一个testcase,都有一个setup和一个teardown的IFuncObject。

          调用的过程基本上是一个广度优先的过程。

          三.实现

          实现其实是很简单地,这里只说一下注册原理

          3.1 注册

          注册是通过TestObject来完成的,TestObject的申明如下:

           

          template<typename Tester, void (Tester::*RealTest) ( void )>
          class TestObject:public IFuncObject

           

          它从IFuncObject继承下来的,接收两个模板参数,一个是tester,另一个是一个成员函数指针。一句

                     DECLARE_TEST(testValue)

          会定义一个TestObject对象,这样在tester被创建的时候,就会自动将这个函数指针注册到tester的对应的TestCase之中。

          3.2 跟踪测试用例

              本来是想用异常来跟踪测试用例的运行状态的,可是发现这样就变得复杂啦。所以这个实现没有用,用了最简单地方式,调用类的全局函数来实现。

          3.3 源码

            源码只包含两个头文件FTest.h和FAssert.h

           

          代码
          /*!
          * Copyright (c) 2010 FengGe(CN), Inc
          * All rights reserved
          * This program is UNPUBLISHED PROPRIETARY property of FengGe(CN).
          * Only for internal distribution.
          *
          * @file: FTest.h
          *
          * @brief: define the framework for unit test
          *
          * @author: li_shugan@126.com
          *
          * @version: 1.0
          *
          * @date: 2010-12-25
          */
          #ifndef _FTEST_H
          #define _FTEST_H
          #include
          <list>
          #include
          <vector>
          #include
          <algorithm>
          #include
          <ostream>
          namespace FTest
          {
          using std::list;
          using std::vector;
          using std::find;
          using std::ostream;
          class Test
          {
          public:
          virtual ~Test(){}
          virtual void run( void ) = 0;
          };

          class TestSuite:public Test
          {
          public:
          static TestSuite* getRootSuite( void )
          {
          if (NULL == g_pRootSuite)
          {
          g_pRootSuite
          = new TestSuite();
          }
          return g_pRootSuite;
          }
          void add(Test *pTest)
          {
          m_listTests.push_back(pTest);
          }

          void remove(Test* pTest)
          {
          m_listTests.erase(find(m_listTests.begin(),m_listTests.end(),pTest));
          }

          virtual void run( void )
          {
          for_each(m_listTests.begin(),m_listTests.end(),RunFun);
          }

          private:
          static void RunFun(Test* itFun)
          {
          itFun
          ->run();
          }
          list
          <Test*> m_listTests;
          static TestSuite* g_pRootSuite;
          };
          TestSuite
          *TestSuite::g_pRootSuite = NULL;


          class IFuncObject
          {
          public:
          virtual void Do( void ) {}
          static IFuncObject NULLFucObject;
          static IFuncObject* NullFunction( void ){return &NULLFucObject;}
          };
          IFuncObject IFuncObject::NULLFucObject
          = IFuncObject();

          class TestCase:public Test
          {
          public:
          TestCase():
          m_funSetup(IFuncObject::NullFunction())
          ,m_funTeardown(IFuncObject::NullFunction())
          {

          }
          virtual void run( void )
          {
          m_funSetup
          ->Do();
          for_each(m_vTestObjects.begin(),m_vTestObjects.end(),
          &RunFunc);
          m_funTeardown
          ->Do();
          }

          void setSetup(IFuncObject *pSetup){m_funSetup = pSetup;}

          void setTearDown(IFuncObject *pTearDown){m_funTeardown = pTearDown;}

          void addTestObject(IFuncObject *pObject){m_vTestObjects.push_back(pObject);}
          private:
          static void RunFunc(IFuncObject* itFun)
          {
          itFun
          ->Do();
          }
          IFuncObject
          *m_funSetup;
          IFuncObject
          *m_funTeardown;
          vector
          <IFuncObject*> m_vTestObjects;
          };

          template
          <typename Tester, void (Tester::*RealTest) ( void )>
          class TestObject:public IFuncObject
          {
          public:
          TestObject(
          void ){ Tester::getCase().addTestObject(this);}
          virtual void Do( void ){(Tester::g_pInstance->*RealTest)();}
          };

          template
          <typename Tester, void (Tester::*Setup) ( void )>
          class SetupObject:public IFuncObject
          {
          public:
          SetupObject(
          void ){ Tester::getCase().addTestObject(this);}
          virtual void Do( void ){(Tester::g_pInstance->*Setup)();}
          };


          template
          <typename Tester, void (Tester::*TearDown) ( void )>
          class TearDownObject:public IFuncObject
          {
          public:
          TearDownObject(
          void ){ Tester::getCase().setTearDown(this);}
          virtual void Do( void ){(Tester::g_pInstance->*TearDown)();}
          };

          template
          <typename Tester>
          class TestCaller
          {
          public:
          TestCaller()
          {
          Tester::g_pInstance
          = new Tester();
          TestSuite::getRootSuite()
          ->add(&Tester::getCase());

          }

          ~TestCaller()
          {
          delete Tester::g_pInstance;
          }
          };
          }

          #define DECLARE_AS_TESTER(tester) typedef tester TestCaseType; \
          static FTest::TestCase __testCase; \
          public: \
          static FTest::TestCase& getCase( void ){return __testCase;} \
          static tester* g_pInstance;

          #define DECLARE_TEST(testName) public: \
          void testName( void ); \
          FTest::TestObject
          <TestCaseType,&TestCaseType::testName> __test##testName; \

          #define DECLARE_SETUP public: \
          void setup( void ); \
          FTest::SetupObject
          <TestCaseType,&TestCaseType::setup> __test##setup;

          #define DECLARE_TEARDOWN public: \
          void tearDwon( void ); \
          FTest::TearDownObject
          <TestCaseType,&TestCaseType::tearDwon> __test##tearDwon;


          #define CALL_TEST(tester) tester* tester::g_pInstance = NULL; \
          FTest::TestCase tester::__testCase
          = FTest::TestCase(); \
          FTest::TestCaller
          <tester> __##tester##callTester;

          #endif //FTEST_H

           

          代码
          /*!
          * Copyright (c) 2010 FengGe(CN), Inc
          * All rights reserved
          * This program is UNPUBLISHED PROPRIETARY property of FengGe(CN).
          * Only for internal distribution.
          *
          * @file: FAssert.h
          *
          * @brief: used for my test framework
          *
          * @author: li_shugan@126.com
          *
          * @version: 1.0
          *
          * @date: 2010-12-25
          */
          #ifndef _FASSERT_H
          #define _FASSERT_H
          #include
          <ostream>
          #include
          <string>
          #include
          <sstream>
          #include
          "FTest.h"

          namespace FTest
          {
          using std::ostream;
          using std::endl;
          using std::string;
          using std::stringstream;

          class TestMain
          {
          public:
          TestMain(ostream
          & rOstream):
          m_pStream(
          &rOstream)
          ,m_nFailedCnt(
          0)
          ,m_nSuccCnt(
          0)
          {
          g_pInstance
          = this;
          }

          ~TestMain( )
          {
          getSteam()
          <<"Total: "<<m_nFailedCnt + m_nSuccCnt << " Success: "<<m_nSuccCnt<<" Failed: "<<m_nFailedCnt<<endl;
          g_pInstance
          = NULL;
          }

          void run()
          {
          TestSuite::getRootSuite()
          ->run();
          }

          public:
          static ostream& getSteam(){return *(g_pInstance->m_pStream);}
          static void addFaildCnt() {++g_pInstance->m_nFailedCnt;}
          static void addSuccCnt() {++g_pInstance->m_nSuccCnt;}

          private:
          ostream
          * m_pStream;
          int m_nFailedCnt;
          int m_nSuccCnt;
          static TestMain* g_pInstance;
          };

          TestMain
          * TestMain::g_pInstance = NULL;

          inline
          void Assert(bool bTrue,const string& strFile,int nLine)
          {
          if (!bTrue)
          {
          TestMain::getSteam()
          <<"Failed at "<<strFile<<"("<<nLine<<")"<<endl;
          TestMain::addFaildCnt();
          }
          else
          {
          TestMain::addSuccCnt();
          }
          }

          template
          <typename T1,typename T2>
          void AssertEqual(const T1& lhs,const T2& rhs,const string& strFile,int nLine)
          {
          if (lhs != rhs)
          {
          TestMain::getSteam()
          <<"Failed at "<<strFile<<nLine<<": expected "<<lhs<<" but "<<rhs<<endl;
          TestMain::addFaildCnt();
          }
          else
          {
          TestMain::addSuccCnt();
          }
          }
          }

          #define ASSERT(b) FTest::Assert(b,__FUNCTION__,__LINE__)
          #define ASSERTFAIL(b) FTest::Assert(!b,__FUNCTION__,__LINE__)
          #define ASSERTEQUAL(lhs,rhs) FTest::AssertEqual(lhs,rhs,__FUNCTION__,__LINE__)

          #endif //FASSERT_H

           

           

           



          相关文章:

          • 2022-01-20
          • 2021-12-13
          • 2022-12-23
          • 2021-12-18
          • 2022-12-23
          • 2021-10-31
          • 2021-07-06
          猜你喜欢
          • 2021-12-04
          • 2022-12-23
          • 2022-12-23
          • 2021-09-05
          • 2022-01-30
          • 2021-11-20
          • 2022-12-23
          相关资源
          相似解决方案