摘自----https://blog.csdn.net/xueyong4712816/article/details/34086649
mockcpp使用方法簡明指導(dǎo)
mock工具的作用是指定函數(shù)的行為(模擬函數(shù)的行為)柱恤×敲希可以對入?yún)⑦M(jìn)行校驗(yàn),對出參進(jìn)行設(shè)定膛壹,還可以指定函數(shù)的返回值脆炎。
(1)mock規(guī)范:每個(gè)MOCKER(function)開始慎颗,跟一系列的.stubs待错、.with拗踢、.will等的內(nèi)容的整體岩臣,稱為一個(gè)mock規(guī)范溜嗜。
(2)核心關(guān)鍵字:指stubs/defaults/expects/before/with/after/will/then/id等這些直接跟在點(diǎn)后面的關(guān)鍵字。
(3)擴(kuò)展關(guān)鍵字:指once()/eq()/check()/returnValue()/repeat()等這些作為核心關(guān)鍵字參數(shù)的關(guān)鍵字架谎。 下面炸宵,請看兩段mockcpp的使用規(guī)范示例代碼,其中帶“/”或者“|”的表示在該位置可能有多種選擇谷扣;帶中括號的表示是可選的土全。
(1) 開源捎琐。
(2) mockcpp支持C函數(shù)的mock和虛接口的mock。
(3) VC下裹匙,mockcpp支持cdecl和stdcall調(diào)用約定的函數(shù)瑞凑。(socket/bind/ftp等函數(shù)就是stdcall調(diào)用約定的)
(4) mockcpp的語法清晰、簡單概页,容易理解(參見下面兩個(gè)sample)籽御。
(5) mockcpp的錯(cuò)誤信息提示非常友好,包含完整的mock規(guī)范定義和實(shí)際運(yùn)行情況(參見下面的樣例)惰匙。
Unexpected invocation: the invocation cannot be found in allowed invoking list.
Invoked: add((int)0x1/1, (int)0x2/2)
Allowed:
method(add)
? .stubs()
? .invoked(0)
? .with(eq((int)0xa/10), eq((int)0x14/20))
? .will(returnValue((int)0xc8/200));
(9) mockcpp是強(qiáng)類型檢查的技掏,強(qiáng)類型檢查也是C/C++的一個(gè)優(yōu)勢,比如eq(3)项鬼,如果調(diào)用函數(shù)時(shí)用的參數(shù)是UL哑梳,那么就應(yīng)該用eq((UL)3),對于函數(shù)返回值也是一樣绘盟,聲明的返回值類型應(yīng)該跟mock中定義的一致鸠真。mock中支持類型檢查,可能會發(fā)現(xiàn)更多代碼BUG的龄毡。
C++強(qiáng)類型檢查弧哎,也是為了提高程序安全性,有些問題通過類型檢查在編譯期可以發(fā)現(xiàn)的稚虎,就不需要在運(yùn)行時(shí)再痛苦的定位了。C/C++的強(qiáng)類型檢查是優(yōu)勢偎捎,我們不用把它拋棄了蠢终。
下面,請看兩段mockcpp的使用規(guī)范示例代碼茴她,其中帶“/”或者“|”的表示在該位置可能有多種選擇寻拂;帶中括號的表示是可選的。
一段簡單的mockcpp使用sample代碼:(帶有完整核心關(guān)鍵字)
TEST(mockcpp simple sample)
{
? ? MOCKER(function) / MOCK_METHOD(mocker, method)
? ? ? ? .stubs() / defaults() / expects(once())
? ? ? ? [.before("some-mocker-id")]
? ? ? ? [.with(eq(3))]
? ? ? ? [.after("some-mocker-id")]
? ? ? ? .will(returnValue(1)) / .will(repeat(1, 20))
? ? ? ? [.then(returnValue(2))]
? ? ? ? [.id("some-mocker-id")]
}
下面是一段比較詳細(xì)的mockcpp使用sample代碼:(帶有完整擴(kuò)展關(guān)鍵字)
TEST(mockcpp detail sample)
{
? ? MOCKER(function) / MOCK_METHOD(mocker, method)
? ? ? ? .stubs() / defaults() / expects(never() | once() | exactly(3) | atLeast(3) | atMost(3) )
? ? ? ? [.before("some-mocker-id")]
? ? ? ? [.with( any() | eq(3) | neq(3) | gt(3) | lt(3) | spy(var_out) | check(check_func)
? ? ? ? ? ? ? ? | outBound(var_out) | outBoundP(var_out_addr, var_size) | mirror(var_in_addr, var_size)
? ? ? ? ? ? ? ? | smirror(string) | contains(string) | startWith(string) | endWith(string) )]
? ? ? ? [.after("some-mocker-id")]
? ? ? ? .will( returnValue(1) | repeat(1, 20) | returnObjectList(r1, r2)
? ? ? ? ? ? ? ? | invoke(func_stub) | ignoreReturnValue()
? ? ? ? ? ? ? ? | increase(from, to) | increase(from) | throws(exception) | die(3))
? ? ? ? [.then(returnValue(2))]
? ? ? ? [.id("some-mocker-id")]
}
注
1丈牢、擴(kuò)展關(guān)鍵字分類: expects里面的叫匹配關(guān)鍵字(Matcher)祭钉; with里面的叫約束關(guān)鍵字(Constraint); will/then里面的叫樁關(guān)鍵字(Stub)己沛。
2慌核、spy的作用是監(jiān)視執(zhí)行該被mock的函數(shù)function被調(diào)用時(shí)傳入的值,會保存在var_out中申尼,供用例中其它地方使用垮卓。
3、outBound的作用是設(shè)置函數(shù)function的出參的值师幕。多半是把該值作為后面部分被測代碼的輸入粟按。(注意與spy區(qū)別)
4、outBoundP,與outBound作用相同灭将,只是用于數(shù)組的情況疼鸟。
5、mirror的作用是對數(shù)組類型的入?yún)⑦M(jìn)行檢查庙曙。(outBoundP是設(shè)置出參的值空镜,兩者是不同的)
6、check的作用是進(jìn)行定制化的入?yún)z查矾利,比如只檢查結(jié)構(gòu)體的部分成員姑裂。可以通過函數(shù)指針或者仿函數(shù)的方式指定男旗,用仿函數(shù)還能預(yù)先保存一些值舶斧,非常方便。(有些mock工具叫它follow)
7察皇、check也能夠用于設(shè)置出參的情況茴厉。
8、die表示程序退出什荣,并且返回指定的值矾缓。它是異常退出,用于模擬一個(gè)函數(shù)調(diào)用崩潰的情況稻爬。
9嗜闻、increase(from, to),表示依次返回from到to的對象桅锄,任何重載了++運(yùn)算符的對象都可以用琉雳。
10、outBound和outBoundP都可以帶一個(gè)約束參數(shù)友瘤,用于對參數(shù)進(jìn)行檢查翠肘,因?yàn)橛型瑫r(shí)作為in和out的參數(shù)。(如:outBound(var_out, eq(3)) )
下面是結(jié)合上面sample的mockcpp使用說明
1辫秧、mock C函數(shù)或者類的靜態(tài)成員方法用MOCKER;
mock 類的非靜態(tài)成員方法需要先用MockObject mocker;聲明一個(gè)mock對象束倍,再用MOCK_METHOD(mocker, method)來mock指定方法。
2盟戏、緊跟著MOCKER/MOCK_METHOD之后的是stubs绪妹、或者defaults、或者expects柿究,三個(gè)必須有一個(gè)喂急。(這是與AMOCK不同的地方,在這個(gè)層次上確定這三個(gè)關(guān)鍵字必須有一個(gè)笛求,可以讓mock語法更清晰)
stubs 表示指定函數(shù)的行為廊移,不校驗(yàn)次數(shù)糕簿。 expects 與stubs的區(qū)別就是校驗(yàn)次數(shù)。(.expects(once()) / .expects(never()) / .expects(exactly(123))) defaults 表示定義一個(gè)默認(rèn)的mock規(guī)范狡孔,但它優(yōu)先級很低懂诗;如果后面有stubs或者expects重新指定函數(shù)行為,就會按照新指定的來運(yùn)行苗膝。(一般用在setup中)
3殃恒、用will指定函數(shù)的返回值;
如果要指定20次調(diào)用都返回1辱揭,則用.will(repeat(1, 20))离唐; 要指定第一次返回1,第二次返回2问窃,第三次返回3亥鬓,就用
? ? ? .will(returnValue(1))
? ? ? .then(returnValue(2))
? ? ? .then(returnValue(3))
如果你指定了前三次的返回值依次為1、2域庇、3嵌戈,那么第四次、第五次調(diào)用听皿,都是返回最后的返回值3熟呛。
4、用id給一個(gè)mock規(guī)范指定一個(gè)名字尉姨,然后可以用after庵朝、before來指定多個(gè)mock應(yīng)該的調(diào)用順序。
注意before在with前又厉,after在with后偿短,id在整個(gè)mock規(guī)范的最后。
5馋没、使用mockcpp時(shí),校驗(yàn)是否按照mock規(guī)范進(jìn)行調(diào)用的降传,應(yīng)該用: GlobalMockObject::verify(); verify之后篷朵,會自動執(zhí)行reset。(如果是對象的mock婆排,應(yīng)該用mocker.verify()声旺,同樣也會自動reset。)
如果單單只想reset段只,也可以:(這很少見腮猖,難道前面你定義的mock規(guī)范都不想要了,也不校驗(yàn)赞枕?那為何要定義呢澈缺?) GlobalMockObject::reset();
一般是在teardown中調(diào)用verify坪创。
6、如果要對某個(gè)函數(shù)(比如add)指定多個(gè)調(diào)用不同的行為(多個(gè)mock規(guī)范)姐赡,比如調(diào)用入?yún)?莱预,2時(shí),返回30项滑;調(diào)用入?yún)?依沮、4時(shí),返回700枪狂。那么可以這樣寫:
MOCKER(add)
? .stubs()
? .with(eq(1), eq(2))
? .will(returnValue(30));
MOCKER(add)
? .stubs()
? .with(eq(3), eq(4))
? .will(returnValue(700));
7危喉、如果對于6的例子,還希望校驗(yàn)次數(shù)州疾,則把stubs改為expects(once())這樣的即可辜限。
8、如果對于6的例子孝治,還希望對調(diào)用順序做嚴(yán)格要求列粪,必須第一次調(diào)用參數(shù)是1、2谈飒,第二次是3岂座、4,那么可以這樣寫:
MOCKER(add)
? .stubs()
? .with(eq(1), eq(2))
? .will(returnValue(30))
? .id("first");
MOCKER(add)
? .stubs()
? .with(eq(3), eq(4))
? .after("first")
? .will(returnValue(700));
9杭措、指定出參的方法(例子中出參為指針類型费什,出參為引用的outBound(var)即可,更加簡單)
void function(int *val)
{
}
應(yīng)該用下面方式來指定出參:
int expect = 10;
MOCKER(function)
? .stubs()
? .with(outBoundP(&expect, sizeof(expect)));
10手素、對同一個(gè)函數(shù)指定多個(gè)mock規(guī)范鸳址,那么這些mock規(guī)范一般是應(yīng)該在輸入方面有差異的,否則沒必要指定多個(gè)泉懦。而且稿黍,在輸入方面無差異的兩個(gè)mock規(guī)范,讓mockcpp內(nèi)部無法判斷是否滿足規(guī)范約束崩哩。 比如巡球,測試函數(shù) void function(int in, int &out),前面一個(gè)是入?yún)⒌肃冢竺嬉粋€(gè)是出參酣栈,希望in為1時(shí),輸出out為100汹押,in為2時(shí)矿筝,輸出out為3,那么應(yīng)該這樣寫:
int out = 100;
MOCKER(function)
? .expects(once())
? .with(eq(1), outBound(out));
out = 3;
MOCKER(function)
? .expects(once())
? .with(eq(2), outBound(out));
假設(shè)你把eq(1)棚贾、eq(2)寫為any()窖维,那么mockcpp會判斷你的兩個(gè)調(diào)用function(1, out)和function(2, out)都符合第一個(gè)mock規(guī)范榆综,從而報(bào)告與你定義的次數(shù)1不匹配。
11陈辱、有時(shí)候需要對同一個(gè)函數(shù)指定多個(gè)mock規(guī)范奖年,并且它們是有規(guī)律的,那么可以借助循環(huán)來簡化代碼沛贪。 假設(shè)要mock的函數(shù)是void function(int in)陋守,希望它依次以0、1利赋、2...10為入?yún)⒈徽{(diào)用水评,每次都調(diào)用一次,那么可以像這樣寫:
MOCKER(function)
? .expects(once())
? .with(eq(0))
? .id("0");
for (int i = 1; i <= 10; i++)
{
? MOCKER(function)
? ? .expects(once())
? ? .with(eq(i))
? ? .after(string(i - 1))
? ? .id(string(i));?
}
12媚送、spy約束關(guān)鍵字應(yīng)用舉例中燥。 spy的作用是監(jiān)視,下面例子塘偎,就是監(jiān)視test在調(diào)用func時(shí)疗涉,傳入的值是多少。有些時(shí)候吟秩,測試用例需要這樣的值咱扣,這種方式是很有效的。
void func(int var)
{
}
void test()
{
? ? int var = 10;
? ? func(var);
}
TEST(sample test)
{
? ? int var;
? ? MOCKER(func)
? ? ? ? .stubs()
? ? ? ? .with(spy(var));
? ? ? ? .with(eq(10));
? ? test();
? ? ASSERT_EQ(var, 10);? ?
}
13涵防、check約束關(guān)鍵字的用法闹伪。 約束關(guān)鍵字中,有很多都是對入?yún)⑦M(jìn)行檢查的壮池,比如eq偏瓤、neq、lt椰憋、gt等等厅克,但它們都只實(shí)現(xiàn)了非常簡單的檢查。 如果用戶想做一個(gè)定制化的檢查橙依,那么就可以用check证舟。 例1:假設(shè)用戶想檢查入?yún)指向的結(jié)構(gòu)的字段b是不是10,那么可以如下這樣寫:
struct AA
{
? ? int a;
? ? int b;
};
void func(int in, AA *p)
{
}
// 實(shí)現(xiàn)一個(gè)檢查函數(shù)票编,入?yún)㈩愋透獧z查的入?yún)⑾嗤祷刂禐閎ool卵渴,返回true表示檢查通過慧域。
bool check_func(AA *p)
{
? ? if ( p->b == 10)
? ? {
? ? ? ? return true;
? ? }
? ? return false;
}
TEST(sample test)
{
? ? MOCKER(func)
? ? ? ? .stubs()
? ? ? ? .with(any(), check(check_func));
? ? func(in, p);
}
例2:如果需要檢查b是否小于某個(gè)給定的數(shù),而且有多個(gè)用例浪读,每個(gè)用例中與b比較的數(shù)不同昔榴,則可以使用仿函數(shù)的方式辛藻。
// 實(shí)現(xiàn)一個(gè)仿函數(shù)類,即重載“()”運(yùn)算符的類互订。
struct CheckFunctor
{
? ? CheckFunctor(int _base) : base(_base){}
? ? bool operator ()(AA *p)
? ? {
? ? ? ? if (p->b < base)
? ? ? ? ? ? ? return true;
? ? ? ? return false;
? ? }
? ? int base;
};
TEST(b should less than 100)
{
? ? MOCKER(func)
? ? ? ? .stubs()
? ? ? ? .with(any(), check(CheckFunctor(100)));
? ? func(in, p);
}
TEST(b should less than 200)
{
? ? MOCKER(func)
? ? ? ? .stubs()
? ? ? ? .with(any(), check(CheckFunctor(200)));
? ? func(in, p);
}
14吱肌、只要打樁了,那么就不會運(yùn)行原函數(shù)仰禽,都是與mock規(guī)范匹配氮墨,運(yùn)行樁。 下面例子中吐葵,對add的入?yún)?规揪、2的情況打樁了,如果調(diào)用了入?yún)?温峭、3的情況猛铅,則會報(bào)告調(diào)用在允許的mock規(guī)范列表中不存在的錯(cuò)誤。
TEST(sample test)
{
? ? MOCKER(add)
? ? ? ? .expects(once())
? ? ? ? .with(eq(1), eq(2))
? ? ? ? .will(returnValue(100));
? ? ASSERT_EQ(100, add(1, 2));
? ? ASSERT_EQ(50, add(2, 3));
}
15凤藏、帶有返回值的函數(shù)奸忽,MOCKER后面必須有will,否則mockcpp認(rèn)為無返回值揖庄,校驗(yàn)時(shí)會發(fā)現(xiàn)返回類型不匹配栗菜。