C++简易测试代码框架

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