Google單元測試工具gtest和gmoke簡介
1、安裝gtest
$sudo apt-get install libgtest-dev
$cd /usr/src/gtest
$sudo cmake .
$sudo make
$sudo cp libgtest*.a /usr/local/lib
2、gtest使用例子
#include<iostream> #include<gtest/gtest.h> using namespace std; struct LinkNode { int _data; LinkNode *_next; LinkNode(const int& data) :_data(data) ,_next(NULL){} }; class Link { public: Link():pHead(new LinkNode(0)) {} void PushBack(const int& data) { if(pHead == NULL) return ; LinkNode *newNode=new LinkNode(data); if(pHead->_next == NULL){ //第一次插入結點 pHead->_next=newNode; } else{ //找到最後一個結點直接尾插 LinkNode *cur=pHead->_next; while(cur->_next) cur=cur->_next; cur->_next=newNode; } } void PopBack() { if(pHead == NULL) return ; LinkNode *cur=pHead; LinkNode *prev=NULL; while(cur->_next) { prev=cur; cur=cur->_next; } prev->_next=NULL; delete cur; } LinkNode *FindNode(const int& data) { if(pHead == NULL) return NULL; LinkNode *cur=pHead->_next; while(cur) { if(cur->_data == data) return cur; cur=cur->_next; } return NULL; } bool Delete(int data) { LinkNode *pos=FindNode(data); if(pos == NULL) return false; LinkNode *cur=pHead->_next; while(cur->_next != pos) cur=cur->_next; cur->_next=pos->_next; delete pos; return true; } void Destroy() { if(pHead == NULL) return; LinkNode *cur=pHead->_next; while(cur) { LinkNode *del=cur; cur=cur->_next; delete del; del=NULL; } delete pHead; //刪除頭結點 } LinkNode *pHead; }; class TestLink: public testing::Test { public: virtual void SetUp() { cout<<"SetUp"<<endl; for(int i=1;i<=5;i++) link.PushBack(i); } virtual void TearDown() { cout<<"TearDown"<<endl; link.Destroy(); } Link link; }; TEST_F(TestLink,PushBack) { ASSERT_FALSE(link.pHead == NULL); link.PushBack(9); LinkNode *res=link.FindNode(9); ASSERT_FALSE(res == NULL); } TEST_F(TestLink,PopBack) { for(int i=1;i<=5;i++) link.PopBack(); } TEST_F(TestLink,FindNode) { ASSERT_TRUE(link.FindNode(3)); ASSERT_TRUE(link.FindNode(2)); ASSERT_TRUE(link.FindNode(4)); ASSERT_TRUE(link.FindNode(5)); ASSERT_TRUE(link.FindNode(1)); ASSERT_FALSE(link.FindNode(7)); } TEST_F(TestLink,Delete12123) { ASSERT_FALSE(link.pHead == NULL); ASSERT_TRUE(link.Delete(3) == true); ASSERT_TRUE(link.Delete(9) == false); } TEST(Test, simple) { ASSERT_EQ(1,1); } int main(int argc,char *argv[]) { testing::InitGoogleTest(&argc,argv); return RUN_ALL_TESTS(); //開始所有測試 }
編譯:
g++ test.cpp -lgtest -lpthread -o test
執行結果:
說明:
1. TEST(test_case_name, test_name) TEST_F(test_fixture, test_name)
TEST巨集的作用是建立一個簡單測試,它定義了一個測試函式,在這個函式裡可以使用任何C++程式碼並使用提供的斷言來進行檢查。
TEST_F與TEST的區別是,TEST_F提供了一個初始化函式(SetUp)和一個清理函式(TearDown),在TEST_F中使用的變數可以在初始化函式SetUp中初始化,在TearDown中銷燬,並且所有的TEST_F是互相獨立的,都是在初始化以後的狀態開始執行,一個TEST_F不會影響另一個TEST_F所使用的資料
2. gtest中,斷言的巨集可以分為兩類,一類是ASSERT系列,一類是EXPECT系列。{ASSERT|EXPECT}_EQ(expected,actual): Tests that expected == actual{ASSERT|EXPECT}_NE(v1,v2): Tests that v1 != v2{ASSERT|EXPECT}_LT(v1,v2): Tests that v1 < v2{ASSERT|EXPECT}_LE(v1,v2): Tests that v1 <= v2{ASSERT|EXPECT}_GT(v1,v2): Tests that v1 > v2
3. ::testing::InitGoogleTest(&argc,argv)gtest的測試案例允許接收一系列的命令列引數,將命令列引數傳遞給gtest,進行一些初始化操作。gtest的命令列引數非常豐富。命令列引數:(1)、--gtest_list_tests:使用這個引數時,將不會執行裡面的測試案例,而是輸出一個案例的列表;(2)、--gtest_filter:對執行的測試案例進行過濾,支援萬用字元;(3)、--gtest_also_run_disabled_tests:執行案例時,同時也執行被置為無效的測試案例;(4)、--gtest_repeat=[COUNT]:設定案例重複執行次數;(5)、--gtest_color=(yes|no|auto):輸出命令列時是否使用一些五顏六色的顏色,預設是auto;(6)、--gtest_print_time:輸出命令時是否列印每個測試案例的執行時間,預設是不列印的;(7)、--gtest_output=xml[:DIRECTORY_PATH\|:FILE_PATH:將測試結果輸出到一個xml中,如—gtest_output=xml:d:\foo.xml 指定輸出到d:\foo.xml ,如果不是指定了特定的檔案路徑,gtest每次輸出的報告不會覆蓋,而會以數字字尾的方式建立;(8)、--gtest_break_on_failure:除錯模式下,當案例失敗時停止,方便除錯;(9)、--gtest_throw_on_failure:當案例失敗時以C++異常的方式丟擲;(10)、--gtest_catch_exceptions:是否捕捉異常,gtest預設是不捕捉異常的,這個引數只在Windows下有效。
4. gtest的事件一共有3種(1)、全域性的,所有案例執行前後;(2)、TestSuite級別的,在某一批案例中第一個案例前,最後一個案例執行後;(3)、TestCase級別的,每個TestCase前後。
全域性事件:要實現全域性事件,必須寫一個類,繼承testing::Environment類,實現裡面的SetUp和TearDown方法。SetUp方法在所有案例執行前執行;TearDown方法在所有案例執行後執行。TestSuite事件:需要寫一個類,繼承testing::Test,然後實現兩個靜態方法:(1)、SetUpTestCase方法在第一個TestCase之前執行;(2)、TearDownTestCase方法在最後一個TestCase之後執行。
TestCase事件:是掛在每個案例執行前後的,需要實現的是SetUp方法和TearDown方法。(1)、SetUp方法在每個TestCase之前執行;(2)、TearDown方法在每個TestCase之後執行。
每個基於gtest的測試過程,是可以分為多個TestSuite級別,而每個TestSuite級別又可以分為多個TestCase級別。這樣分層的結構的好處,是可以針對不同的TestSuite級別或者TestCase級別設定不同的引數、事件機制等,並且可以與實際測試的各個模組層級相互對應,便於管理。
5. 可以通過操作符"<<"將一些自定義的資訊輸出
如在EXPECT_EQ(v1, v2)<< "thisis a error! "
6. 引數化
必須新增一個類,繼承testing::TestWithParam<T>,其中T就是你需要引數化的引數型別。
7. 編寫死亡測試案例
TEST的第一個引數,即test_case_name,請使用DeathTest字尾,原因是gtest會優先執行死亡測試案例,應該是為執行緒安全考慮。
8. testing::AddGlobalTestEnvironment(newFooEnvironment)
在main函式中建立和註冊全域性環境物件。
3、安裝gmock
$sudo apt-get installgoogle-mock
$cd /usr/src/gmock
$sudo cmake .
$sudo make
$sudo cp lib*.a /usr/local/lib
4、使用gmock例子
Messenger.h
#ifndef SRC_MESSENGER_H_
#define SRC_MESSENGER_H_
#include <string>
using namespace std;
class Messenger
{
public:
virtual ~Messenger() {}
virtual string getMessage() = 0;
};
#endif /* SRC_MESSENGER_H_ */
MockMessenger.h
#ifndef SRC_MOCKMESSENGER_H_
#define SRC_MOCKMESSENGER_H_
#include "Messenger.h"
#include <string>
#include <gmock/gmock.h>
using namespace std;
class MockMessenger : public Messenger
{
public:
MOCK_METHOD0(getMessage, string());
};
#endif /* SRC_MOCKMESSENGER_H_ */
HelloWorld.cpp
#include "HelloWorld.h"
#include "Messenger.h"
HelloWorld::HelloWorld()
{
}
HelloWorld::~HelloWorld()
{
}
string HelloWorld::getMessage(Messenger* messenger) const
{
return messenger->getMessage();
}
HelloWorld.h
#ifndef SRC_HELLOWORLD_H_
#define SRC_HELLOWORLD_H_
#include <string>
#include "MockMessenger.h"
using namespace std;
class HelloWorld
{
public:
HelloWorld();
virtual ~HelloWorld();
string getMessage(Messenger* messenger) const;
};
#endif /* SRC_HELLOWORLD_H_ */
HelloWorld_UnitTest.cpp#include "HelloWorld.h"
#include <gtest/gtest.h>
#include <gmock/gmock.h>
#include "MockMessenger.h"
#include <string>
#include <memory>
using namespace testing;
TEST(HelloWorldTest, getMessage)
{
MockMessenger messenger;
std::string msg = "Hello World";
EXPECT_CALL(messenger, getMessage()).WillRepeatedly(Return(ByRef(msg)));
HelloWorld helloWorld;
EXPECT_EQ("Hello World", helloWorld.getMessage(&messenger));
EXPECT_EQ("Hello World", helloWorld.getMessage(&messenger));
EXPECT_EQ("Hello World", helloWorld.getMessage(&messenger));
}
int main(int argc, char** argv)
{
testing::InitGoogleMock(&argc, argv);
return RUN_ALL_TESTS();
}
編譯:
g++ HelloWorld.cpp HelloWorld_UnitTest.cpp -lgtest -lpthread -lgmock -o test
執行結果:
說明:
Google Mock的作用是幫你快速地做出一個介面的仿製品。如果你的設計依賴其它的類,而這些類還沒有完成或非常昂貴(如資料庫);
如果你要測試你的模組與其它模組是否能正確結合,並想了解其互動過程;那麼Google Mock就能幫助你。
例子中Messenger.h是一個沒有完成功能的介面,由MocMessenger.h模擬完成
幾個巨集的說明:
MOCK_METHOD#1(#2, #3(#4) )
#1表示你要mock的方法共有幾個引數
#2是你要mock的方法名稱
#3表示這個方法的返回值型別
#4是這個方法具體的引數
以上就已經完成了對介面類的mock,但打樁的工作還沒有全部完成,還需要設定某個成員函式執行時能按我們的期望來返回值,我們繼續往下看。
選擇一個要進行單元測試的檔案s_ls_dosomething.cpp,裡面只有一個函式:
再新建一個s_ls_dosomething_ut.cpp作為s_ls_dosomething.cpp這個檔案中函式單元測試檔案,內容如下:
之前在完成interface_mock.cpp的時候說打樁還沒有全部完成,剩下的工作就在這個TEST中了,就是通過ON_CALL/ EXPECT_CALL來設定樁函式的返回值,這2個巨集的具體說明如下:
ON_CALL(#1, #2(#3)).WillByDefault(Return(#4));
#1表示mock物件
#2表示想定義的那個方法名稱。
#3表示定義方法的引數,只有當傳入的引數=#3的值時,才會生效,也可用_,表示匹配任何引數輸入都生效
#4表示呼叫要返回值。
除了ON_CALL以外,還有EXPECT_CALL(#1, #2(#3)),引數含義同ON_CALL,還可以有額外的功能,比如:
EXPECT_CALL(#1, #2(#3)).Times(5).WillOnce(Return(100))
.WillOnce(Return(150))
.WillRepeatedly(Return(200));
表示第一次返回100,第二次呼叫返回150,後面全部返回200,以此類推。
ON_CALL和EXCEPT_CALL相當於設定樁函式的屬性設定,因此應當在樁函式呼叫前進行設定。