如下圖
CMakeLists.txt
googletest:googletest源文件
test_template:模塊的單元測試文件夾
CMakeLists.txt
main.cpp:運行該文件夾中所有.cpp文件的測試
test_add.cpp:模塊中add功能的測試文件,其中包括該功能的具體測試,如下圖
首先當然是cmake和make。
make test
,便會利用ctest運行所有模塊的單元測試注意事項:
Googletest主要包括兩方面:gtest和gmock
3.1. gtestgtest主要包括兩種方式:TEST和TEST_F
兩者區別,TEST宏的作用是創建一個簡單測試,而TEST_F宏用于在多個測試中使用同樣的數據配置,所以它又叫 測試夾具(Test Fixtures)每個測試都將從測試類做派生。
由于TEST_F更多是用在C++上,因此本單元測試主要使用TEST。
TEST(分類名, 測試名) {測試代碼
}
通過測試下面add_int函數,舉例說明
int add_int(int x,int y){return x+y;
}
想要測試add模塊的add_int功能。如下
//對于add_int函數的測試 分類為TestAddInt,具體創建三個測試,分別測試輸入值的三種情況
//輸入值同為正數(測試名為add_int_positive)
TEST(TestAddInt, add_int_positive) {int ret=add_int(10,24);
EXPECT_EQ(ret,34);
}
//輸入值同為負數(測試名為add_int_negative)
TEST(TestAddInt, add_int_negative) {int ret=add_int(-10,-24);
EXPECT_EQ(ret,-34);
}
//輸入值一正一負(測試名為add_int_nega_posi)
TEST(TestAddInt, add_int_nega_posi) {int ret=add_int(-10,24);
EXPECT_EQ(ret,14);
}
//通過調用add_int函數,并利用EXPECT_EQ比較輸出結果是否符合預期,從而達到測試目的
其中的EXPECT_EQ為斷言的宏,Gtest中,斷言的宏可以理解為分為兩類:
通常情況應該選使用EXPECT_,因為ASSERT_*在報告完錯誤后不會進行清理工作,有可能導致內存泄露問題
常用的斷言宏有以下六類(具體請查看附錄)
mock測試就是在測試過程中,對于某些不容易構造或者不容易獲取的對象,用一個虛擬的對象來創建以便測試的測試方法。
如:在進行單元測試時,我們想要測試自己的函數A
,但是函數A
卻依賴于函數B
,當函數B
無法滿足預期時就無法對函數A
進行測試,主要由于下面幾個原因:
函數B
依賴于硬件設備函數B
的返回值無法滿足我們的預期函數B
尚未實現這時就需要對函數B
進行打樁(仿真mock),使其達到我們預期的效果。
而這時就涉及到了函數的多態。C++可以在不修改函數A
的情況下,通過虛函數實現動態多態,將調用函數B
修改為調用仿真mock函數。而C語言不支持虛函數。
因此,需要手動將函數A
調用函數B
,通過函數指針,修改為調用仿真mock函數。
因為會對代碼進行改動,所以在利用gmock做單元測試時,建議能少用就少用,可以自己在測試函數中模擬數據。
真要用的時候,記得在測試完成后,將改動的代碼恢復原樣。
例子如下:
//函數A,調用函數B去處理num
int test_A(int num){if(test_B(num)<=100){return 0;
}
return 1;
}
//函數B處理num
int test_B(int num){num+=50;
return num;
}
1.先構建結構體,包裝輸入函數B的參數以及函數指針
typedef struct ctest{int num;//輸入函數B的參數
int (*p_test_B)(struct ctest*);//指針指向 返回值為int型,參數為ctest類型指針 的函數
}ctest;
2.修改函數A中的函數調用,將函數B替換為函數指針
int test_A(ctest* p_c_struct){//將參數修改為結構體ctest
if(p_c_struct->p_test_B(p_c_struct->num)<=100){//修改函數的調用
return 0;
}
return 1;
}
上述兩個步驟,需要在源碼中進行修改?。。。。。。?!
后續步驟在測試文件中
3.創建mock類,并定義mock方法
class Mock_FOO{public:
//定義mock方法
MOCK_METHOD1(mock_test_B,int (ctest* p_c_struct));
//其中MOCK_METHOD1最后的數字1代表參數有一個,同理若要定義一個無參數的mock方法,則MOCK_METHOD0
//mock_test_B為定義的mock函數名
//int 表示該函數返回值
//ctest* p_c_struct表示該函數的參數
};
4.實例化mock對象,并創建mock對象方法的函數的C包裝
//實例化mock對象
Mock_FOO mocker;
//創建mock對象方法的函數的C包裝
int mock_test_B(ctest* p_c_struct){return mocker.mock_test_B(p_c_struct);
}
5.創建測試例子
TEST(TestMock, test_mock) {EXPECT_CALL(mocker, mock_test_B(An())).WillRepeatedly(Return (1000));
//EXPECT_CALL是mock最主要的部分,配合.WillRepeatedly等限制可以實現在調用該mock函數時,自定義模擬各種效果!
//這句的最終效果即為,每當test_A調用mock_test_B時,都會返回1000
//具體EXPECT_CALL解釋請看附錄!??!
ctest c_struct_foo;
c_struct_foo.p_test_B = mock_test_B;
int ret=test_A(&c_struct_foo);
EXPECT_EQ(ret,1);
}
具體EXPECT_CALL的介紹請看附錄?。。。。?!
4. 附錄 4.1. 常用的斷言宏 4.1.1. 布爾型檢查Nonfatal assertion | Verifie |
---|---|
EXPECT_TRUE(condition); | condition is true |
EXPECT_FALSE(condition); | condition is false |
Nonfatal assertion | Verifie |
---|---|
EXPECT_EQ(expected, actual); | expected == actual |
EXPECT_NE(val1, val2); | val1 != val2 |
EXPECT_LT(val1, val2); | val1< val2 |
EXPECT_LE(val1, val2); | val1<= val2 |
EXPECT_GT(val1, val2); | val1 >val2 |
EXPECT_GE(val1, val2); | val1 >= val2 |
Nonfatal assertion | Verifie |
---|---|
EXPECT_FLOAT_EQ(expected, actual); | the two float values are almost equal |
EXPECT_DOUBLE_EQ(expected, actual); | the two double values are almost equal |
在對比數據方面,我們往往會討論到浮點數的對比。因為在一些情況下,浮點數的計算精度將影響對比結果。“幾乎相等”是指兩個值彼此相差 4 個 ULP
4.1.4. 相近值檢查Nonfatal assertion | Verifie |
---|---|
EXPECT_NEAR(val1, val2, abs_error); | the difference between val1 and val2 doesn’t exceed the given absolute error |
例子如下
//測試10+24=34,而36是否在誤差5的范圍內
TEST(TestNEAR, test_NEAR) {int ret=add_int(10,24);
EXPECT_NEAR(ret,36,5);
}
注:整型時包含邊界,浮點時因為精度問題,不包含邊界
如EXPECT_NEAR(34,36,2);//測試成功
如EXPECT_NEAR(34.1 , 34.2 ,0.1);//測試失敗
Nonfatal assertion | Verifie |
---|---|
EXPECT_STREQ(expected_str,actual_str); | the two C strings have the same content |
EXPECT_STRNE(str1, str2); | the two C strings have different content |
EXPECT_STRCASEEQ(expected_str, actual_str); | the two C strings have the same content, ignoring case (忽略大小寫) |
EXPECT_STRCASENE(str1, str2); | the two C strings have different content, ignoring case (忽略大小寫) |
STREQ和STRNE同時支持char和wchar_t類型的,STRCASEEQ和STRCASENE卻只接收char*,例子如下
char* test_char( ){char* a="hi";
return a;
}
//測試test_char返回值是否為"hi"
TEST(TestChar, test_char_EQ) {char* b=test_char();
EXPECT_STREQ(b,"hi");
}
//忽略大小寫
TEST(TestChar, test_char_CASEEQ) {char* b=test_char();
EXPECT_STRCASEEQ(b,"Hi");
}
4.2. EXPECT_CALL的具體介紹EXPECT_CALL(mock_object, method(matcher1, matcher2, ...))
.With(multi_argument_matcher)
.Times(cardinality)
.InSequence(sequences)
.After(expectations)
.WillOnce(action)
.WillRepeatedly(action)
.RetiresOnSaturation();
第1行的mock_object就是Mock類的對象
第1行的method(matcher1, matcher2, …)中的method就是Mock類中的某個方法名,比如上述的mock_test_B;而matcher(匹配器)的意思是定義方法參數的類型,后面詳細介紹。
第3行的Times(cardinality)的意思是之前定義的method運行幾次。至于cardinality的定義,也會在后面詳細介紹。
第4行的InSequence(sequences)的意思是定義這個方法被執行順序(優先級),后面舉例說明。
第6行WillOnce(action)是定義一次調用時所產生的行為,比如定義該方法返回怎么樣的值等等。
第7行WillRepeatedly(action)的意思是缺省/重復行為。
結合該3.2中gmock的TEST:EXPECT_CALL(mocker, mock_test_B(An())).WillRepeatedly(Return (1000));
//EXPECT_CALL(mocker, mock_test_B(An()))
//mocker即為mock對象
//mock_test_B即為創建的mock方法。
//An()為匹配器,其中的An表示可以是type類型的任意值,在這里表示ctest *類型的任意值
//.WillRepeatedly(Return (1000))
//表示每次調用時都會返回1000
4.2.1. 匹配器(matcher)用于定義Mock類中的方法的形參的值
包含以下幾種類型
匹配器 | 解釋 |
---|---|
_ | 可以代表任意類型 |
A() or An() | 可以是type類型的任意值 |
匹配器 | 解釋 |
---|---|
Eq(value) 或者 value | argument == value,method中的形參必須是value |
Ge(value) | argument >= value,method中的形參必須大于等于value |
Gt(value) | argument >value |
Le(value) | argument<= value |
Lt(value) | argument< value |
Ne(value) | argument != value |
IsNull() | method的形參必須是NULL指針 |
NotNull() | argument is a non-null pointer |
Ref(variable) | 形參是variable的引用 |
TypedEq(value) | 形參的類型必須是type類型,而且值必須是value |
匹配器 | 解釋 |
---|---|
DoubleEq(a_double) | 形參是一個double類型,比如值近似于a_double,兩個NaN是不相等的 |
FloatEq(a_float) | 同上,只不過類型是float |
NanSensitiveDoubleEq(a_double) | 形參是一個double類型,比如值近似于a_double,兩個NaN是相等的,這個是用戶所希望的方式 |
NanSensitiveFloatEq(a_float) | 同上,只不過形參是float |
這里的字符串即可以是C風格的字符串,也可以是C++風格的。
匹配器 | 解釋 |
---|---|
ContainsRegex(string) | 形參匹配給定的正則表達式 |
EndsWith(suffix) | 形參以suffix截尾 |
HasSubstr(string) | 形參有string這個子串 |
MatchesRegex(string) | 從第一個字符到最后一個字符都完全匹配給定的正則表達式. |
StartsWith(prefix) | 形參以prefix開始 |
StrCaseEq(string) | 參數等于string,并且忽略大小寫 |
StrCaseNe(string) | 參數不是string,并且忽略大小寫 |
StrEq(string) | 參數等于string |
StrNe(string) | 參數不等于string |
基數用于Times()中來指定模擬函數將被調用多少次
基數 | 解釋 |
---|---|
AnyNumber() | 函數可以被調用任意次. |
AtLeast(n) | 預計至少調用n次. |
AtMost(n) | 預計至多調用n次. |
Between(m, n) | 預計調用次數在m和n(包括n)之間. |
Exactly(n) 或 n | 預計精確調用n次. 特別是, 當n為0時,函數應該永遠不被調用. |
Actions(行為)用于指定Mock類的方法所期望模擬的行為:比如返回什么樣的值、對引用、指針賦上怎么樣個值,等等。
行為 | 解釋 |
---|---|
Return() | 讓Mock方法返回一個void結果 |
Return(value) | 返回值value |
ReturnNull() | 返回一個NULL指針 |
ReturnRef(variable) | 返回variable的引用. |
ReturnPointee(ptr) | 返回一個指向ptr的指針 |
行為 | 解釋 |
---|---|
Assign(&variable, value) | 將value分配給variable |
行為 | 解釋 |
---|---|
Invoke(f) | 使用模擬函數的參數調用f, 這里的f可以是全局/靜態函數或函數對象. |
Invoke(object_pointer, &class::method) | 使用模擬函數的參數調用object_pointer對象的mothod方法. |
行為 | 解釋 |
---|---|
DoAll(a1, a2, …, an) | 每次發動時執行a1到an的所有動作. |
IgnoreResult(a) | 執行動作a并忽略它的返回值. a不能返回void. |
你是否還在尋找穩定的海外服務器提供商?創新互聯www.cdcxhl.cn海外機房具備T級流量清洗系統配攻擊溯源,準確流量調度確保服務器高可用性,企業級服務器適合批量采購,新人活動首月15元起,快前往官網查看詳情吧
分享題目:googletest在C語言項目中的使用指南-創新互聯
地址分享:http://m.kartarina.com/article22/ihjcc.html
成都網站建設公司_創新互聯,為您提供用戶體驗、品牌網站建設、網站設計公司、軟件開發、網站建設、ChatGPT
聲明:本網站發布的內容(圖片、視頻和文字)以用戶投稿、用戶轉載內容為主,如果涉及侵權請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網站立場,如需處理請聯系客服。電話:028-86922220;郵箱:631063699@qq.com。內容未經允許不得轉載,或轉載時需注明來源: 創新互聯