C語言[工程專案應用]gtest測試框架編寫以及自定義測試框架
gtest測試框架編寫以及自定義測試框架
1 gtest測試框架:
下載地址: git clone https://github.com/google/googletest
安裝部署:
cd googletest
cmake CmakeLists.txt #生成makefile
make #執行makefile
目錄結構:
googletest
----lib
--------libgtest.a
----CMakeList.txt
----makefile
----googletest
--------include
------------gtest
----------------gtest.h
1.1 測試樣例
專案目錄構成:
project
----include
--------gtest
------------gtest.h
----lib
--------libgtest.a
----main.cpp
main.cpp
:
#include <stdio.h>
#include <gtest/gtest>
int add(int a, int b) {
return a + b;
}
// gtest.h 的 內部巨集
TEST(func, add) {
EXPECT_EQ(add(3, 4), 7);
EXPECT_EQ(add(3, 1), 5);
ASSERT_EQ(add(3, 1), 5);
}
int main(int args, char *argv[]) {
// gtest的內部名稱空間
testing::InitGoogleTest(&argc, argv);
// RUN_ALL_TESTS 也應為 gtest.h 的內部巨集
return RUN_ALL_TESTS();
}
該案例用於檢測 add(3, 4)是否為7, add(3,1)是否為5, add(3,1) = 5是否出錯
編譯與執行:
g++ -std=c++11 -I./include main.cpp -L./lib -lgtest -lpthread
-std=c++11
: 指定編譯標準為C++11
-I./include
: 指定標頭檔案地址
-L./lib
: 指定靜態庫地址
-lgtest
: 呼叫靜態庫 libgtest.a
-lpthread
: 呼叫靜態庫 libpthread.a
結果 :
[==========] Running 1 test from 1 test suite.
[----------] Global test environment set-up.
[----------] 1 test from func
[ RUN ] func.add
main.cpp:10: Failure
Expected equality of these values:
add(3, 1)
Which is: 4
5
main.cpp:12: Failure
Expected equality of these values:
add(3, 1)
Which is: 4
6
[ FAILED ] func.add (0 ms)
[----------] 1 test from func (0 ms total)
[----------] Global test environment tear-down
[==========] 1 test from 1 test suite ran. (0 ms total)
[ PASSED ] 0 tests.
[ FAILED ] 1 test, listed below:
[ FAILED ] func.add
1 FAILED TEST
2 自定義測試框架
要求:
- 設計TEST(a , b)巨集
- 設計RUN_ALL_TESTS()巨集
- 設計EXPECT_EQ(…)等系列功能
- 實現程式碼變色
- 實現動態記憶體分佈
主體架構:
main_my.c
----test.h
----test.c
makefile
bin
----a.out
2.1 主檔案main_my.c
:
#include <studio.h>
#inlcude "test.h"
int add(int a, int b) {
return a + b;
}
// 測試用例組的整體巨集替換
TEST(testfunc, add1) {
// 測試單元的具體巨集替換
EXPECT_EQ(add(3, 4), 7);
EXPECT_NE(add(3, 2), 5);
EXPECT_EQ(add(3, 3), 6);
}
TEST(testfunc, add2) {
EXPECT_EQ(add(3, 4), 7);
EXPECT_LT(add(3, 1), 5);
EXPECT_EQ(add(3, 3), 6);
}
TEST(test, funcadd1) {
EXPECT_EQ(add(3, 4), 7);
EXPECT_NE(add(1, 1), 5);
EXPECT_EQ(add(3, 3), 6);
}
int main(int argc, char *argv[]) {
// 執行所有測試用例
return RUN_ALL_TESTS();
}
2.2 標頭檔案include/test.h
:
PART 1
#ifndef _TEST_H
#define _TEST_H
// 動態空間分佈引入的自定義連結串列庫
#include "linklist.h"
// TEST巨集定義
#define TEST(a, b)\
void a##_haizei_##b();\
__attribute__((constructor)) void add##_haizei_##a##_haizei_##b() {\
add_function(a##_haizei_##b, #a"."#b);\
}\
void a##_haizei_##b()
/* 1
* a : 變數名1, b : 變數名2
* 2
* __attribute__(constructor) type func(...) {...}
* executing func before function main
* (add the TEST and compose the name(add_function) before main)
* 3
* ## : concat a and b as ab
*/
#的用法:
- 將多個變數名連線成一個新的變數(函式)名
例 :a##_haizei_##b()
==a_haizei_b
- 取變數名的字串
例:#a"."#b
=="a.b"
TEST巨集實現 :
位於 : 主函式的前面
功能 : 1. 宣告單元測試函式;2. 定義單元測試用例新增函式
技巧:
__attribute__((constructor)) func
機制 :func
先於main
執行
int main(int argc, char * argv[]) {
@autoreleasepool {
printf("main function");
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}
__attribute__((constructor)) static void beforeFunction()
{
printf("beforeFunction\n");
}
// 輸出結果順序
beforeFunction
main function
__attribute__
為GNU C的特色機制 : 可以設定 函式, 變數, 型別的屬性
https://www.jianshu.com/p/dd425b9dc9db
PART 2
// 輸出顏色巨集設定
#define COLOR(a, b) "\033[" #b "m" a "\033[0m"
#define COLOR_HL(a, b) "\033[1;" #b "m" a "\033[0m"
#define GREEN(a) COLOR(a, 32)
#define RED(a) COLOR(a, 31)
#define BLUE(a) COLOR(a, 34)
#define YELLOW(a) COLOR(a, 33)
#define GREEN_HL(a) COLOR_HL(a, 32)
#define RED_HL(a) COLOR_HL(a, 31)
#define BLUE_HL(a) COLOR_HL(a, 34)
#define YELLOW_HL(a) COLOR_HL(a, 33)
/* printf中設定顏色的方式, 其中A為被上色的字元
* 正常亮度:
* "\033[xxmA\033[0m"
* 高亮:
* "\033[1;xxmA\033[0m"
*/
PART 3
// 泛型巨集: _Generic的使用 (為生成的值設定顏色)
// 只能用於.c檔案中 (在.cpp & .cc檔案中使用會報錯)
// 依照變數a的型別返回對應的字串
#define TYPE(a) _Generic((a), int:"%d", double:"%lf")
// 利用泛型巨集 和 顏色函式名巨集替換 對生成值設定顏色
// a: 生成值, color: 待替換顏色函式名
#define P(a, color) {\
char frm[1000];\
sprintf(frm, color("%s"), TYPE(a));\
printf(frm, a);\
}
泛型巨集
_Generic
的使用 :
_Generic
是C11新增的一個關鍵字
_Generic((var), type1 : ..., type2 : ..., ……, default : ...)
type 表示型別,…表示對這個型別的var進行的操作,最終的default表示其他型別,也就是所定義的type中沒有的型別,就會跳到default
PART 4
// 實際單元測試用例的部分共有功能的巨集定義
/* EXPECT(a, b, cmp)
* a : 單元用例的實際函式返回值;
* b : 單元用例的期望值;
* cmp : 用例的操作符
*/
#define EXPECT(a, b, cmp) {\
printf(GREEN("[-----------] ") #a" " #cmp" "#b" ");\
haizei_test_info.total += 1;\
__typeof(a) _a = (a);\
__typeof(b) _b = (b);\
if (_a cmp _b) {\
haizei_test_info.success += 1;\
printf("%s\n",\
(_a) cmp (_b) ? GREEN_HL("True") : RED_HL("False"));\
}\
else {\
printf("%s\n",\
(_a) cmp (_b) ? GREEN_HL("True") : RED_HL("False"));\
printf("\n");\
printf(YELLOW_HL("\t%s:%d: failure\n"), __FILE__, __LINE__);\
printf(YELLOW_HL("\t\texpect : " #a " " #cmp " " #b "\n"));\
printf(YELLOW_HL("\t\tactual : " ));\
P(_a, YELLOW_HL);\
printf("\n\n");\
}\
}
// 測試用例的具體設定
#define EXPECT_EQ(a, b) EXPECT(a, b, ==)
#define EXPECT_NE(a, b) EXPECT(a, b, !=)
#define EXPECT_LT(a, b) EXPECT(a, b, <)
#define EXPECT_LE(a, b) EXPECT(a, b, <=)
#define EXPECT_GT(a, b) EXPECT(a, b, >)
#define EXPECT_GE(a, b) EXPECT(a, b, >=)
詳細註釋
:
EXPECT(a, b, cmp) {
// a , b被變數替換, cmp被邏輯比較符替換
printf(GREEN("[-----------] ") #a" " #cmp" "#b" ");
// GREEN("...")將返回一個帶顏色的字串 並與(例)"5 >= 2"相連
haizei_test_info.total += 1;
// 更新測試用例計數
__typeof(a) _a = (a);
__typeof(b) _b = (b);
typeof() :
GUN C提供的一種特性,可以取得變數的型別,或者表示式的型別
在巨集定義中動態獲取相關結構體成員的型別 :
例:
1 .定義一個和變數x
相同型別的臨時變數_max1
,定義一個和變數y
相同型別的臨時變數_max2
2 .判斷兩者型別是否一致,不一致給出一個警告,最後比較兩者。
#define max(x, y) ({ \
typeof(x) _max1 = (x); \
typeof(y) _max2 = (y); \
(void) (&_max1 == &_max2); \//如果呼叫者傳參時兩者型別不一致,編譯時會警告。
_max1 > _max2 ? _max1 : _max2; })
if (_a cmp _b) {
// 表示用例通過, 更新用例通過計數, 並按要求設定列印資訊
haizei_test_info.success += 1;
printf("%s\n",
(_a) cmp (_b) ? GREEN_HL("True") : RED_HL("False"));
}
else {
// 列印用例出錯處的位置資訊與程式碼資訊, 並列印實際輸出值的資訊進行對比
printf("%s\n",
(_a) cmp (_b) ? GREEN_HL("True") : RED_HL("False"));
printf("\n");
printf(YELLOW_HL("\t%s:%d: failure\n"), __FILE__, __LINE__);
printf(YELLOW_HL("\t\texpect : " #a " " #cmp " " #b "\n"));
printf(YELLOW_HL("\t\tactual : " ));
P(_a, YELLOW_HL);
// _a 與實際生成右值的型別相等, YELLOW_HL來替換顏色函式名
printf("\n\n");
}
}
PART 5
// 以下宣告內容的定義將位於../src/test.c檔案中
// 設定測試用例指標 : 用於計數 與 儲存
typedef void (*Testfunc)();
// 儲存測試用例函式指標, 函式字串名, 以及連結串列資訊
typedef struct Function {
Testfunc func;
const char* str;
struct LinkNode p;
} Function;
// 用於記錄測試用例總數與通過次數的結構體
struct FunctionInfo {
int total, success;
};
// 宣告外部變數,其具體定義會在後面的檔案中出現
extern struct FunctionInfo haizei_test_info;
// 宣告主函式中的方法
int RUN_ALL_TESTS();
// 宣告將測試用錄進行記錄儲存的方法
void add_function(Testfunc, const char*);
#endif
2.3 自定義連結串列標頭檔案include/linklist.h
#ifndef _LinkList_H
#define _LinkList_H
// 獲取 T 型別 name欄位的偏移量
#define offset(T, name) (long long)( &( ((T *)(0))->name ) )
// p : 當前節點欄位p的地址, T, name (可獲取接下來T型別的name欄位的偏移量)
// 因為地址的數字表示比較長, 所以需要log long來匹配
#define Head(p, T, name) (T *)( (char *)(p) - offset(T, name) )
struct LinkNode {
struct LinkNode *next;
};
#endif
offset(T, name)
:
- 將空地址
0
強轉成T *
型別 :(T *)(0)
- 取
T
對應的name
成員變數的地址 :&(((T *)(0))->name)
- 地址取
long long
型別
Head(p, T, name)
需結合後續操作進行解讀
以上兩步是為了將含LinkNode
型別成員的結構體進行連線
2.4 功能函式檔案src/test.c
#include "../include/test.h"
#include <string.h>
#inlcude <stdio.h>
#include <math.h>
#include <stdlib.h>
// 設定起始例項儲存節點, 並設定末尾節點進行初始化
Function func_head, *func_tail = &func_head;
// 對應 include/test.h 中的 extern struct FunctionInfo haizei_test_info;
struct FunctionInfo haizei_test_info;
// 主函式的實際操作
int RUN_ALL_TESTS() {
// 遍歷之前在之前已經執行過的add_function所儲存的各用例所處的地址資訊(以便於呼叫)
for (struct LinkNode *p = func_head.p.next; p; p = p->next) {
// 獲得當用例小組的指標, 列印開始資訊 與 對應函式名
Function *func = Head(p, Function, p);
printf(GREEN("[====RUN====]") YELLOW_HL(" %s") "\n", func->str);
// 初始化用例計數
haizei_test_info.total = 0;
haizei_test_info.success = 0;
// 執行函式
func->func();
// 計算用例通過率
double rate = 100.0 * haizei_test_info.success / haizei_test_info.total;
printf(GREEN("[__"));
if (fabs(rate - 100.0) < 1e-6) {
printf(BLUE_HL("%6.2lf%%"), rate);
}
else {
printf(YELLOW_HL("%6.2lf%%"), rate);
}
printf(GREEN("__]") "total : %d success : %d\n",
haizei_test_info.total, haizei_test_info.success
);
}
return 0;
}
// 儲存用例資訊的連結串列生成函式
void add_function(Testfunc func, const char* str) {
struct Function *temp = (Function *)calloc(1, sizeof(Function));
temp->func = func;
temp->str = strdup(str);
func_tail->p.next = &(temp->p);
func_tail = temp;
}
strdup()
與strcpy()
的區別:
strdup()
可以把要複製的內容直接複製給沒有初始化的指標,它會自動分配空間給目的指標,但需要手動free()
進行記憶體回收。
strcpy()
的目的指標一定是已經分配(足夠)記憶體的指標
2.5 編譯執行檔案makefile
.PHONY:clean run
all: main_my.o src/test.o include/test.h
gcc -I./ main_my.o src/test.o -o ./bin/my
main.o: main_my.c include/test.h
gcc -I./ -c main_my.c
haizei/test.o: haizei/test.c haizei/test.h
gcc -I./ -c src/test.c -o src/test.o
clean:
rm -rf bin/my main_my.o src/test.o
run:
./bin/my
相關文章
- 【編測編學】分享一套好用的功能測試用例編寫框架框架
- 如何編寫介面測試用例?測試工程師必備技能!工程師
- Hypium框架使能ArkTS應用高效測試框架
- 關於Electron框架應用的安全測試框架
- 自動化測試如此容易!多語言自動化測試框架 Selenium 程式設計(C#篇)框架程式設計C#
- 介面測試--自定義斷言設定
- 應用<測試專案>官網
- 前端測試框架Jest——語法篇前端框架
- 前端測試框架前端框架
- 測試用例編寫方法
- 介面測試用例編寫和測試關注點
- 以太坊Solidity程式語言開發框架————8、測試合約Solid框架
- api測試框架 GuardianAPI框架
- 前端測試框架 Jest前端框架
- Web測試框架SeleniumBaseWeb框架
- python測試框架-pytestPython框架
- 介面測試框架Requests框架
- 大型專案裡Flutter測試應用例項整合測試深度使用Flutter
- 編寫自定義 Laravel 擴充套件包測試用例,phpunit 錯誤提示 “class not found”Laravel套件PHP
- 萬能測試用例及測試用例編寫方法(待更新)
- TestNG測試框架之失敗測試重跑框架
- Pytest單元測試框架——Pytest+Allure+Jenkins的應用框架Jenkins
- JavaScript單元測試框架JavaScript框架
- React測試框架之enzymeReact框架
- Google 單元測試框架Go框架
- 介面測試框架選擇框架
- 自動化測試框架框架
- 單元測試框架 mockito框架Mockito
- 介面測試之unittest框架框架
- 介面測試框架實戰(三)| JSON 請求與響應斷言框架JSON
- 測試工程師必備:掌握這5種設計方法快速編寫測試用例~思路分析工程師
- 如何優雅編寫測試用例
- 手寫 Hibernate ORM 框架 05-基本效果測試ORM框架
- Flutter自定義View以及響應式UI框架原理FlutterViewUI框架
- 測試工程師必學:測試人員如何深入瞭解專案工程師
- 介面測試框架接入效能測試實踐分享框架
- 自動化測試中如何判斷測試是否透過?詳解 Pytest 測試框架的斷言用法框架
- Golang 編寫測試教程Golang