Personal programming prefers refactoring, refactoring can improve the quality of your code and make it clearer to read. However, there is a problem with refactoring, that is, how to ensure that the reconstruction of the implementation of the function with the code before the reconstruction of the same, if each refactoring after the completion of this indifference, there will be great risk, if each refactoring, the side of the test, then the workload will be huge, and ultimately may be even if the code has the desire to refactor Not to refactor. Unless the code can be tested automatically. The actual test is the interface, not all the code, as long as the interface remains unchanged, the workload of automated testing is not as large as imagined. In fact, when we are in unit testing, we will test all kinds of anomalies, except that they are not written in the test code.
In Java, there are JUnit, in C # NUnit, in C + +, I do not know what the automated testing tools (the author's ignorant). The idea of writing an automated test program was created. The automated test program should essentially be a command-mode application case where multiple command objects are stored, and the command objects store the test functions and, when needed, run the command objects, According to the execution results of these command objects, the test is passed. The first is to define the Command object. The command object is relatively simple and is defined as follows:
typedef std::function<bool (testinfo&) > Testfun;
This is a return value of a Boolean type, the input parameter is a testinfo reference to the function object, and if the return value returns True to indicate that the test passed, false indicates that the test failed. TestInfo is not complex, it mainly contains some information about this test, including test properties and test results. Test properties have the name of the test, some descriptions, and whether or not to expect to throw an exception, and the test result is whether the test was successful. The TestInfo code is as follows:
/*** @brief Test information objects, save test information and test results. **/class testinfo{public: testinfo () {level = 1; name = ""; SubName = ""; IsOK = false; Iswantexception = false; Remark = ""; } Public: int. level ; /**< test Case Level */ std::string name; /**< Test Interface Name */ std::string subname; /**< Test Interface Name Specific description */ bool IsOK; /**< Test Results */ bool iswantexception; /**< whether to expect abnormal occurrence */ std::string remark; /**< Note information */};
With the command object, there is also a place to store and run the command object, so I added a class called Testbaseex, the reason is called EX, because I have implemented a Testbase class, and later on testbase based on the improvement, The name is programmed with Testbaseex. In Testbaseex, the command object is stored in a vector, and a ontest method is provided to run the command objects.
The /*** @brief test the underlying class. **/class testbaseex{public:typedef Std::function<bool (testinfo&) > Testfun; /** * @brief perform the test. * @param [in] testshow test result display function * */void OnTest (Std::function<void (testinfo&) > Testshow) { for (Auto it = M_tests.begin (); It! = M_tests.end (); ++it) {TestInfo info; try {bool result = (*it) (info); if (info.iswantexception) {Info.isok = false; } else {Info.isok = result; }} catch (...) {info.exception = "There is an exception"; if (info.iswantexception) {Info.isok = true; }} testshow (info); }}public:std::vector<testfun> m_tests;};
Testbaseex is mainly a OnTest method, the method logic is relatively simple: loop through the Command object and execute, if you expect to throw an exception without throwing an exception, the test department passes, otherwise, according to the return value to determine whether the test passed. The incoming Testshow function object is responsible for processing the test results of the command object (typically, if it is shown as green, does not pass, and is shown in red). When creating a command object, the better the information is, the better it is to include functions, parameters, and so on, so a macro is added to create the Command object.
/*** @brief Add test objects. **/#define TEST_INIT (info, sub) { ostringstream oss; oss<< "Position:" <<__FILE__<< "-" <<__LINE__<< "-" <<__FUNCTION__<<endl; Info.name = __function__;/*oss.str (); */} info.subname = sub; Info.remark = ""; Info.isok = true; #define TESTFUN_INIT (name) m_tests.push_back (Std::bind (&name, this, std::tr1::p laceholders::_1) )
The real test class inherits from Testbaseex, such as the Hidb test class:
/*** @brief database Operation Test class. **/class hisdbtest:public testbaseex{public: hisdbtest (); ~hisdbtest ();p rivate: /** * @brief perform the Open Interface test (the connection string is correct). * @param [in] info test data Object * @retval true: Success, FALSE, failure * /bool OnOpen (testinfo& info); BOOL Droptable (testinfo&); BOOL CreateTable (testinfo&); BOOL Addrecorder (testinfo&); BOOL AddRecorder2 (testinfo&); BOOL Scalar (testinfo&); BOOL Scalar2 (testinfo&); BOOL Scalar3 (testinfo&); BOOL Readrecorders (testinfo&); BOOL ReadRecorders2 (testinfo&); BOOL ReadRecorders3 (testinfo&); BOOL Deleterecorder (testinfo&); BOOL OnClose (testinfo&); BOOL Onclose_repeat (testinfo&); BOOL OnConnRelace2 (testinfo&); BOOL OnConnRelace3 (testinfo&);p rivate: hidb* m_db;};
Implementation (here and my previous article Hidb correspondence, this is my long ago Hidb version, is more complex than the present):
using namespace Std; Hisdbtest::hisdbtest () {testfun_init (Hisdbtest::onopen); Testfun_init (hisdbtest::D roptable); Testfun_init (hisdbtest::createtable); Testfun_init (Hisdbtest::addrecorder); Testfun_init (HISDBTEST::ADDRECORDER2); Testfun_init (Hisdbtest::scalar); Testfun_init (HISDBTEST::SCALAR2); Testfun_init (HISDBTEST::SCALAR3); Testfun_init (hisdbtest::readrecorders); Testfun_init (HISDBTEST::READRECORDERS2); Testfun_init (HISDBTEST::READRECORDERS3); Testfun_init (hisdbtest::D eleterecorder); Testfun_init (HISDBTEST::ONCONNRELACE2); Testfun_init (HISDBTEST::ONCONNRELACE3); this->m_db = new Hidb (Hidbtype_mysql, false); }hisdbtest::~hisdbtest () {if (this->m_db) {this->m_db->close (); Delete this->m_db; this->m_db = NULL; }}bool Hisdbtest::onopen (testinfo& info) {Test_init (info, "Open database"); Info.remark = "(Please provide database: host=127.0.0.1;port=3306;") "DBNAME=TEST;USER=ROOT;PWD=ROOT;CHARSET=GBK;"; Return This->m_db->open ("HOST=127.0.0.1;PORT=3306;DBNAME=TEST;USER=ROOT;PWD=ROOT;CHARSET=GBK;") );} BOOL Hisdbtest::D roptable (testinfo& info) {Test_init (info, "Executenoquery no Reference"); Return This->m_db->executenoquery ("drop table if exists table1;"); BOOL Hisdbtest::createtable (testinfo& info) {Test_init (info, "Executenoquery no Reference"); Return This->m_db->executenoquery ("CREATE TABLE table1 (column1 varchar (6) NOT NULL," "Column2 varcha R (All) not NULL, "" "Column3 int. NOT NULL default 1," "Column4 int," "column5 timestamp NOT NULL Defau Lt Current_timestamp, "column6 varchar", primary key (Column1)); BOOL Hisdbtest::addrecorder (testinfo& info) {Test_init (info, "Executenoquery C language (printf)"); Return This->m_db->executenoquery ("INSERT into table1 (column1,column2,column3,column4,column6)" "VAL UES ('%s ', '%s ',%d, NULL, '%s ') "," MyTest "," My second Test recordEr ",", "This test is create by Xuminrong");} BOOL Hisdbtest::addrecorder2 (testinfo& info) {Test_init (info, "Create method, automatically compose SQL statement"); VectorSource code: http://download.csdn.net/detail/xumingxsh/7791923
This article turns itself to another blog "Upstream Sailing"