1. 程式人生 > 實用技巧 >單元測試框架的原理和實現(模仿google test)

單元測試框架的原理和實現(模仿google test)

2020/7/14

學習內容:

  1. 單元測試框架的編寫與實現,單元測試框架屬於測試的一部分,驗證程式的某一部分邏輯是否正確。

  2. 模仿google test模組的方式,在檔案中加入多個TEST函式,然後呼叫RUN_ALL_TESTS函式就可以執行所有的TEST函式,並且返回結果;

  3. TEST函式其實並不是真正意義上的一個函式,而是通過巨集定義實現的一個檔案頭。因為多個TEST函式存在於同一個檔案中肯定是不符合c語言的語法的。

    通過以下程式碼定義TEST

    #define TEST(a, b) void a_##_b()  //定義了一個函式頭
    #define EXPECT(a, comp ,b) {\
    	if (!((a) comp (b))) { \
    		test_func_flag = 1; \		//用於標記測試是否出錯,全域性變數
    		expect_printf(__FILE__, __LINE__, "(" #a ") " #comp " (" #b ")"); \
    	}  \
    }
    #define EXPECT_EQ(a, b) EXPECT(a, ==, b);		//這裡使用了一個技巧
    #define EXPECT_NE(a, b) EXPECT(a, !=, b);
    
    //但是這樣我們不知道有幾個測試用例,如何執行測試用例
    //使用attribute註冊函式使函式先於main函式執行
    //將TEST(a, b)改為如下
    #define TEST(a, b) \
    void a##_##b();\		//這一句是宣告這個測試函式,不然在註冊函式時會出現未宣告
    __attribute__((constructor)) \  //先於main函式執行
    void add_##a##_##b() {  \
        add_func(a##_##b, #a "." #b); \
    }\
    void a##_##b()			//原來的函式頭放在了這裡
      
    //接下來實現一些介面函式和結構
    struct Func_Data {
        void (*func)();
        const char *func_name;
    } func_arr[100];		//真正的工程中是不可以用一個固定大小的陣列的
    int func_cnt;  
    
    void add_func(void (*func)(), const char *func_name) {
        func_arr[func_cnt].func = func;
        func_arr[func_cnt].func_name = func_name;
        func_cnt++;
        return;		//禮貌性的返回
    }
    
    int test_func_flag;
    
    void expect_printf(const char *file, int line_on, const char *msg) {
        printf(YELLOW"  %s:%d : Failure\n", file, line_on);
        printf(YELLOW"    Expected: %s\n"NONE, msg);
        return;
    }
    
    int RUN_ALL_TEST() {
        printf(GREEN"[==========]"NONE" Running %d tests\n", func_cnt);
        for (int i = 0; i < func_cnt; i++) {
            printf(GREEN"[   RUN     ] %s\n", func_arr[i].func_name);
            test_func_flag = 0;
            int b = clock();
            func_arr[i].func();
            int e = clock();
            if (test_func_flag) {
                printf(RED"[   FAILED  ]\n"NONE);
            } else {
                printf(GREEN"[      OK   ]\n"NONE);
            }
            printf(" %s (%s ms)\n", func[i].func_name, 1000 * (e - b) / CLOCK_PER_SEC);
        }
        return 0;
    }
    
    
    //將測試函式封裝在testcasen.h中
    #ifdef DEBUG
    	#include "testcase.h"
    #endif
    //...
    #ifdef DEBUG
    	return RUN_ALL_TESTS();
    #else
    	return 0;
    #endif
    
    //makefile 的編寫
    release:
    debug:  -DDEBUG
    

最終實現的效果