在大多數狀況下,咱們寫了一個函數,爲了驗證這個函數的正確性,咱們還須要寫不少的測試代碼。可用於C/C++單元測試的框架有不少,什麼cpptest, gtest等等不可勝數。他們很強大,能夠很方便的拿來使用到咱們的項目中。可是有的時候,個人項目很小,或者說個人函數功能不多,小到運行的時間比框架啓動的時間都還要短,這個時候個人目的很簡單,就是要以最簡單的形式,加入幾行代碼測試功能便可,以下:程序員
TEST(func1()); TEST(func2()); ... TEST(func3());
畢竟大多數程序員所在的公司都沒有或不多有專門的白盒測試人員,幾乎都要本身寫測試代碼。cpptest,gtest雖然也很簡單,可是仍是要編譯,導入,而且會在系統中耗掉一部分時間。比如共享單車同樣,我就是想如今騎車出去耍一圈,我立刻,馬上就要騎,然而你要我花費一點時間去借一輛自行車,這個時間我不肯意花,等我找到的時候或許我激情也沒有了。算法
在個人項目中,有時候建立一個C++工程就僅僅是爲了寫一個算法而已,這個時候若是能簡單到導入一個頭文件,加幾個宏定義,就能完成測試,那該可能是多美好的事情。儘管寫不少 if (condition) then 這樣的語句很簡單,但打印出來的效果實在太難看,也很耗費時間。在此基礎上,作了一個小型的測試代碼封裝,只須要包含兩個文件,寫幾行宏定義便可完成代碼測試。windows
.h頭文件:框架
#ifndef CPPTEST_H #define CPPTEST_H // This is a simple test-driver framework for C++ /* * Example: * * TEST_SUIT_BEGIN; * * TEST_UNIT_BEGIN("function_test1"); * TEST_CHECK(xpod_common::equal(10.0, 10-0.2, 0.1), "10.0, 10-0.2, 0.1"); * TEST_CHECK(xpod_common::equal(10.0, 10-0.1, 0.1), "10.0, 10-0.1, 0.1"); * TEST_CHECK(xpod_common::equal(10.0, 10-0.0, 0.1), "10.0, 10-0.0, 0.1"); * TEST_UNIT_END; * * TEST_UNIT_BEGIN("function_test2"); * TEST_CHECK(xpod_common::equal(10.0, 10-0.2, 0.1), "10.0, 10-0.2, 0.1"); * TEST_CHECK(xpod_common::equal(10.0, 10-0.1, 0.1), "10.0, 10-0.1, 0.1"); * TEST_CHECK(xpod_common::equal(10.0, 10-0.0, 0.1), "10.0, 10-0.0, 0.1"); * TEST_UNIT_END; * * TEST_SUIT_END; * */ #define TEST_SUIT_BEGIN TSuit::instance()->begin() #define TEST_SUIT_END TSuit::instance()->end(); #define TEST_UNIT_BEGIN(name) \ { \ TUnit unit(name); \ unit.begin() #define TEST_CHECK(condition, msg) \ unit.testCheck(condition, msg) #define TEST_UNIT_END \ unit.end(); \ TSuit::instance()->addNumAll(unit.numAll()); \ TSuit::instance()->addNumOK(unit.numOK()); \ TSuit::instance()->addUnit(); \ } #include <ctime> #include <string> using namespace std; class TUnit { public: TUnit(const char* name):_name(name){} unsigned int numOK(){return _numOK;} unsigned int numAll(){return _numAll;} void testCheck(bool bCheck, const char* msg); void begin(); void end(); private: unsigned int _numOK = 0; unsigned int _numAll = 0; string _name; }; class TSuit { public: static TSuit* instance(); public: void addNumAll(unsigned int count){_numAll+=count;} void addNumOK(unsigned int count){_numOK+=count;} void addUnit(){++_numUnit;} void begin(); void end(); private: TSuit(){} clock_t _tstart; unsigned int _numOK = 0; unsigned int _numAll = 0; unsigned int _numUnit = 0; }; #endif // CPPTEST_H
.cpp源文件:函數
#include "cpptest.h" #include <windows.h> string date_time() { time_t rawtime; time(&rawtime); struct tm * timeinfo = localtime (&rawtime); char buffer[256] = {0}; strftime(buffer, 256, "[%Y-%m-%d %H:%M:%S]", timeinfo); return string(buffer); } void printWarning(const char* msg) { SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), BACKGROUND_RED|BACKGROUND_GREEN); printf("%s\n", msg); SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 7); } void printError(const char* msg) { SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), BACKGROUND_RED); printf("%s\n", msg); SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 7); } void printResult(bool bOK) { SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), bOK?BACKGROUND_GREEN:BACKGROUND_RED); printf(bOK ? "[ OK ]" : "[ FAILED ]"); SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 7); printf(" "); } void TUnit::testCheck(bool bCheck, const char* msg) { printResult(bCheck); printf("%s\n", (msg!=nullptr)?msg:" "); _numOK += bCheck ? 1 : 0; _numAll += 1; } void TUnit::begin() { printf("[ %s ]\n-----------------------------------------------------\n", _name.data()); } void TUnit::end() { printf("-----------------------------------------------------\nTotal:%d, Success:%d, Failed:%d\n\n", _numAll, _numOK, _numAll-_numOK); } TSuit *TSuit::instance() { static TSuit suit; return &suit; } void TSuit::begin() { _numOK = 0; _numAll = 0; _numUnit = 0; _tstart = clock(); printf("Test Suit Begin %s\n-----------------------------------------------------\n\n", date_time().data()); } void TSuit::end() { printf("Test Suit End %s\n-----------------------------------------------------\n", date_time().data()); printf("[Test Result] Unit:%d, Total:%d, Success:%d, Failed:%d, CostTime:%.2f s\n", _numUnit, _numAll, _numOK, _numAll-_numOK, (clock()-_tstart)/1000.0f); }
兩個文件加起來不過150多行程序,僅定義一個單元測試TUnit和TSuit類,前者用來記錄一個單元測試的結果狀況,後者用來記錄總體測試結果,在Windows控制檯支持彩色打印效果,還有執行測試的所有時間。單元測試
下面爲執行測試的代碼:測試
void test_unit() { TEST_UNIT_BEGIN("123"); TEST_CHECK(xpod_common::equal(10.0, 10-0.2, 0.15), "10.0, 10-0.2, 0.15"); TEST_CHECK(xpod_common::equal(10.0, 10-0.1, 0.15), "10.0, 10-0.1, 0.15"); TEST_CHECK(xpod_common::equal(10.0, 10-0.0, 0.15), "10.0, 10-0.0, 0.15"); TEST_CHECK(xpod_common::equal(10.0, 10+0.1, 0.15), "10.0, 10+0.1, 0.15"); TEST_CHECK(xpod_common::equal(10.0, 10+0.2, 0.15), "10.0, 10+0.2, 0.15"); TEST_UNIT_END; } int main(int argc, char *argv[]) { TEST_SUIT_BEGIN; test_unit(); TEST_UNIT_BEGIN("equal"); TEST_CHECK(xpod_common::equal(10.0, 10-0.2, 0.1), "10.0, 10-0.2, 0.1"); TEST_CHECK(xpod_common::equal(10.0, 10-0.1, 0.1), "10.0, 10-0.1, 0.1"); TEST_CHECK(xpod_common::equal(10.0, 10-0.0, 0.1), "10.0, 10-0.0, 0.1"); TEST_CHECK(xpod_common::equal(10.0, 10+0.1, 0.1), "10.0, 10+0.1, 0.1"); TEST_CHECK(xpod_common::equal(10.0, 10+0.2, 0.1), "10.0, 10+0.2, 0.1"); TEST_UNIT_END; TEST_UNIT_BEGIN("equal"); for (auto i=0; i<20; i++) { char buffer[128] = {0}; sprintf(buffer, "10.0, 9+0.1*%d, 0.1", i); TEST_CHECK(xpod_common::equal(10.0, 9+0.1*i, 0.1), buffer); } TEST_UNIT_END; TEST_SUIT_END; return 0; }
使用很是簡單,固然功能也很簡單,沒法和cpptest,gtest這樣的大型測試框架相媲美,但用到個人算法測試項目中正合適。後續有時間將增長UNIX版本的打印接口,若是能有邊界測試宏那就更好了,測試算法輸入接口不用再寫for語句了。ui
最後來看看其中的一個測試效果:spa