googletest在C語言項目中的使用指南-創(chuàng)新互聯(lián)

文章目錄
  • 1. gtest文件層級劃分與說明
    • 1.1. 單元測試運行方法
  • 2. 新增單元測試
    • 2.1. 新增模塊測試
    • 2.2. 新增模塊功能測試
  • 3. 單元測試具體實現(xiàn)
    • 3.1. gtest
    • 3.2. gmock
  • 4. 附錄
    • 4.1. 常用的斷言宏
      • 4.1.1. 布爾型檢查
      • 4.1.2. 二值檢查
      • 4.1.3. 浮點檢查
      • 4.1.4. 相近值檢查
      • 4.1.5. 字符串檢查
    • 4.2. EXPECT_CALL的具體介紹
      • 4.2.1. 匹配器(matcher)
      • 4.2.2. 基數(shù)(Cardinalities)
      • 4.2.3. 行為(Actions)

參考:
關于EXPECT_CALL
關于TEST的斷言宏
關于gtest的C語言封裝

創(chuàng)新互聯(lián)建站專注于中大型企業(yè)的網(wǎng)站建設、網(wǎng)站制作和網(wǎng)站改版、網(wǎng)站營銷服務,追求商業(yè)策劃與數(shù)據(jù)分析、創(chuàng)意藝術與技術開發(fā)的融合,累計客戶千余家,服務滿意度達97%。幫助廣大客戶順利對接上互聯(lián)網(wǎng)浪潮,準確優(yōu)選出符合自己需要的互聯(lián)網(wǎng)運用,我們將一直專注品牌網(wǎng)站制作和互聯(lián)網(wǎng)程序開發(fā),在前進的路上,與客戶一起成長!1. gtest文件層級劃分與說明
  1. 第一層:按模塊劃分
  2. 第二層:模塊內(nèi)按功能劃分
  3. 第三層:模塊內(nèi)各功能的具體測試

如下圖
CMakeLists.txt
googletest:googletest源文件
test_template:模塊的單元測試文件夾
CMakeLists.txt
main.cpp:運行該文件夾中所有.cpp文件的測試
test_add.cpp:模塊中add功能的測試文件,其中包括該功能的具體測試,如下圖

1.1. 單元測試運行方法

首先當然是cmake和make。

  • 當想運行所有模塊單元測試時。
    build文件夾中直接make test,便會利用ctest運行所有模塊的單元測試
  • 當想運行單個模塊的單元測試時。(包含顯示每個具體測試的結果)
    進入build/gtest/bin/文件夾中運行相應模塊的測試進程。
2. 新增單元測試 2.1. 新增模塊測試
  1. 在gtest文件夾中新建模塊文件夾,其中包含CMakeLists.txt,main文件,各功能測試文件。
    (可直接復制模板文件夾,再修改CMakeLists.txt的project名即可)
  2. 在gtest的CMakeLists.txt中add_subdirectiry(新增模塊)。
    如下圖
2.2. 新增模塊功能測試
  • 直接新建.cpp文件
  • include gtest/gtest.h和gmock/gmock.h頭文件
  • using namespace testing命名空間說明

注意事項:

  1. 一定要.cpp結尾
    因為Googletest主要是針對C++的單元測試(當然,C語言項目也能使用)。
  2. 一定要using namespace testing
3. 單元測試具體實現(xiàn)

Googletest主要包括兩方面:gtest和gmock

3.1. gtest

gtest主要包括兩種方式:TEST和TEST_F
兩者區(qū)別,TEST宏的作用是創(chuàng)建一個簡單測試,而TEST_F宏用于在多個測試中使用同樣的數(shù)據(jù)配置,所以它又叫 測試夾具(Test Fixtures)每個測試都將從測試類做派生。
由于TEST_F更多是用在C++上,因此本單元測試主要使用TEST。

TEST(分類名, 測試名) {測試代碼
}

通過測試下面add_int函數(shù),舉例說明

int add_int(int x,int y){return x+y;
}

想要測試add模塊的add_int功能。如下

//對于add_int函數(shù)的測試 分類為TestAddInt,具體創(chuàng)建三個測試,分別測試輸入值的三種情況
//輸入值同為正數(shù)(測試名為add_int_positive)
TEST(TestAddInt, add_int_positive) {int ret=add_int(10,24);
    EXPECT_EQ(ret,34);
}
//輸入值同為負數(shù)(測試名為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);
}
//通過調(diào)用add_int函數(shù),并利用EXPECT_EQ比較輸出結果是否符合預期,從而達到測試目的

其中的EXPECT_EQ為斷言的宏,Gtest中,斷言的宏可以理解為分為兩類:

  • ASSERT_* 系列,當檢查點失敗時,退出當前函數(shù)(注意:并非退出當前案例)。
  • EXPECT_* 系列,當檢查點失敗時,繼續(xù)執(zhí)行下一個檢查點(每一個斷言表示一個測試點)

通常情況應該選使用EXPECT_,因為ASSERT_*在報告完錯誤后不會進行清理工作,有可能導致內(nèi)存泄露問題

常用的斷言宏有以下六類(具體請查看附錄)

  • 布爾型檢查
  • 二值檢查
  • 浮點檢查
  • 相近值檢查
  • 字符串檢查
  • 異常檢查 (不適用,因為要使用C++的throw,雞肋)
3.2. gmock

mock測試就是在測試過程中,對于某些不容易構造或者不容易獲取的對象,用一個虛擬的對象來創(chuàng)建以便測試的測試方法。
如:在進行單元測試時,我們想要測試自己的函數(shù)A,但是函數(shù)A卻依賴于函數(shù)B,當函數(shù)B無法滿足預期時就無法對函數(shù)A進行測試,主要由于下面幾個原因:

  • 函數(shù)B依賴于硬件設備
  • 真實的函數(shù)B的返回值無法滿足我們的預期
  • 團隊開發(fā)中函數(shù)B尚未實現(xiàn)

這時就需要對函數(shù)B進行打樁(仿真mock),使其達到我們預期的效果。
而這時就涉及到了函數(shù)的多態(tài)。C++可以在不修改函數(shù)A的情況下,通過虛函數(shù)實現(xiàn)動態(tài)多態(tài),將調(diào)用函數(shù)B修改為調(diào)用仿真mock函數(shù)。而C語言不支持虛函數(shù)。
因此,需要手動將函數(shù)A調(diào)用函數(shù)B,通過函數(shù)指針,修改為調(diào)用仿真mock函數(shù)。

因為會對代碼進行改動,所以在利用gmock做單元測試時,建議能少用就少用,可以自己在測試函數(shù)中模擬數(shù)據(jù)。
真要用的時候,記得在測試完成后,將改動的代碼恢復原樣。
例子如下:

//函數(shù)A,調(diào)用函數(shù)B去處理num
int test_A(int num){if(test_B(num)<=100){return 0;
	}
	return 1;
}
//函數(shù)B處理num
int test_B(int num){num+=50;
	return num;
}



1.先構建結構體,包裝輸入函數(shù)B的參數(shù)以及函數(shù)指針
typedef struct ctest{int num;//輸入函數(shù)B的參數(shù)
	int (*p_test_B)(struct ctest*);//指針指向 返回值為int型,參數(shù)為ctest類型指針 的函數(shù)
}ctest;

2.修改函數(shù)A中的函數(shù)調(diào)用,將函數(shù)B替換為函數(shù)指針
int test_A(ctest* p_c_struct){//將參數(shù)修改為結構體ctest
	if(p_c_struct->p_test_B(p_c_struct->num)<=100){//修改函數(shù)的調(diào)用
		return 0;
	}
	return 1;
}


上述兩個步驟,需要在源碼中進行修改?。。。。。。?!
后續(xù)步驟在測試文件中


3.創(chuàng)建mock類,并定義mock方法
class Mock_FOO{public:
	//定義mock方法
	MOCK_METHOD1(mock_test_B,int (ctest* p_c_struct));
		//其中MOCK_METHOD1最后的數(shù)字1代表參數(shù)有一個,同理若要定義一個無參數(shù)的mock方法,則MOCK_METHOD0
		//mock_test_B為定義的mock函數(shù)名
		//int 表示該函數(shù)返回值
		//ctest* p_c_struct表示該函數(shù)的參數(shù)
};

4.實例化mock對象,并創(chuàng)建mock對象方法的函數(shù)的C包裝
//實例化mock對象
Mock_FOO mocker;
//創(chuàng)建mock對象方法的函數(shù)的C包裝
int mock_test_B(ctest* p_c_struct){return mocker.mock_test_B(p_c_struct);
}

5.創(chuàng)建測試例子
TEST(TestMock, test_mock) {EXPECT_CALL(mocker, mock_test_B(An())).WillRepeatedly(Return (1000));
	//EXPECT_CALL是mock最主要的部分,配合.WillRepeatedly等限制可以實現(xiàn)在調(diào)用該mock函數(shù)時,自定義模擬各種效果!
	//這句的最終效果即為,每當test_A調(diào)用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 assertionVerifie
EXPECT_TRUE(condition);condition is true
EXPECT_FALSE(condition);condition is false
4.1.2. 二值檢查
Nonfatal assertionVerifie
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
4.1.3. 浮點檢查
Nonfatal assertionVerifie
EXPECT_FLOAT_EQ(expected, actual);the two float values are almost equal
EXPECT_DOUBLE_EQ(expected, actual);the two double values are almost equal

在對比數(shù)據(jù)方面,我們往往會討論到浮點數(shù)的對比。因為在一些情況下,浮點數(shù)的計算精度將影響對比結果。“幾乎相等”是指兩個值彼此相差 4 個 ULP

4.1.4. 相近值檢查
Nonfatal assertionVerifie
EXPECT_NEAR(val1, val2, abs_error);the difference between val1 and val2 doesn’t exceed the given absolute error

例子如下

//測試10+24=34,而36是否在誤差5的范圍內(nèi)
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);//測試失敗

4.1.5. 字符串檢查
Nonfatal assertionVerifie
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 (忽略大小寫)

STREQSTRNE同時支持char和wchar_t類型的,STRCASEEQSTRCASENE卻只接收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(匹配器)的意思是定義方法參數(shù)的類型,后面詳細介紹。
第3行的Times(cardinality)的意思是之前定義的method運行幾次。至于cardinality的定義,也會在后面詳細介紹。
第4行的InSequence(sequences)的意思是定義這個方法被執(zhí)行順序(優(yōu)先級),后面舉例說明。
第6行WillOnce(action)是定義一次調(diào)用時所產(chǎn)生的行為,比如定義該方法返回怎么樣的值等等。
第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即為創(chuàng)建的mock方法。
	//An()為匹配器,其中的An表示可以是type類型的任意值,在這里表示ctest *類型的任意值
//.WillRepeatedly(Return (1000))
	//表示每次調(diào)用時都會返回1000
4.2.1. 匹配器(matcher)

用于定義Mock類中的方法的形參的值
包含以下幾種類型

  • 通用
匹配器解釋
_可以代表任意類型
A() or An()可以是type類型的任意值
  • 一般比較
匹配器解釋
Eq(value) 或者 valueargument == 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
  • 浮點數(shù)的比較
匹配器解釋
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)參數(shù)等于string,并且忽略大小寫
StrCaseNe(string)參數(shù)不是string,并且忽略大小寫
StrEq(string)參數(shù)等于string
StrNe(string)參數(shù)不等于string
  • 容器的匹配
    關于STL,這里就不說明了。
4.2.2. 基數(shù)(Cardinalities)

基數(shù)用于Times()中來指定模擬函數(shù)將被調(diào)用多少次

基數(shù)解釋
AnyNumber()函數(shù)可以被調(diào)用任意次.
AtLeast(n)預計至少調(diào)用n次.
AtMost(n)預計至多調(diào)用n次.
Between(m, n)預計調(diào)用次數(shù)在m和n(包括n)之間.
Exactly(n) 或 n預計精確調(diào)用n次. 特別是, 當n為0時,函數(shù)應該永遠不被調(diào)用.
4.2.3. 行為(Actions)

Actions(行為)用于指定Mock類的方法所期望模擬的行為:比如返回什么樣的值、對引用、指針賦上怎么樣個值,等等。

  • 值的返回
行為解釋
Return()讓Mock方法返回一個void結果
Return(value)返回值value
ReturnNull()返回一個NULL指針
ReturnRef(variable)返回variable的引用.
ReturnPointee(ptr)返回一個指向ptr的指針
  • 分配值
行為解釋
Assign(&variable, value)將value分配給variable
  • 使用函數(shù)或者函數(shù)對象(Functor)作為行為
行為解釋
Invoke(f)使用模擬函數(shù)的參數(shù)調(diào)用f, 這里的f可以是全局/靜態(tài)函數(shù)或函數(shù)對象.
Invoke(object_pointer, &class::method)使用模擬函數(shù)的參數(shù)調(diào)用object_pointer對象的mothod方法.
  • 復合動作
行為解釋
DoAll(a1, a2, …, an)每次發(fā)動時執(zhí)行a1到an的所有動作.
IgnoreResult(a)執(zhí)行動作a并忽略它的返回值. a不能返回void.

你是否還在尋找穩(wěn)定的海外服務器提供商?創(chuàng)新互聯(lián)www.cdcxhl.cn海外機房具備T級流量清洗系統(tǒng)配攻擊溯源,準確流量調(diào)度確保服務器高可用性,企業(yè)級服務器適合批量采購,新人活動首月15元起,快前往官網(wǎng)查看詳情吧

分享題目:googletest在C語言項目中的使用指南-創(chuàng)新互聯(lián)
地址分享:http://bm7419.com/article22/ihjcc.html

成都網(wǎng)站建設公司_創(chuàng)新互聯(lián),為您提供用戶體驗、品牌網(wǎng)站建設、網(wǎng)站設計公司軟件開發(fā)、網(wǎng)站建設ChatGPT

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉載內(nèi)容為主,如果涉及侵權請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉載,或轉載時需注明來源: 創(chuàng)新互聯(lián)

網(wǎng)站優(yōu)化排名