在大多数状况下,咱们写了一个函数,为了验证这个函数的正确性,咱们还须要写不少的测试代码。可用于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