Google C++ Mocking Framework Cheat Sheet中文版
· 定義一個模擬(Mock)類
· 模擬(Mocking)普通類
· 模擬(Mocking)模板類
· 為模擬函數(Mock Functions)指定調用約定
· 在測試中使用模擬
· 設置默認動作(Default Actions)
· 設置預期(Expectations)
· 匹配器(Matchers)
· 通配符
· 一般比較
· 浮點數匹配器
· 字符串匹配器
· 容器匹配器
· 成員匹配器
· 匹配函數或函數對象的返回值
· 指針匹配器
· 使用函數或函數對象作為匹配器
· 多參數匹配器
· 復合匹配器
· 轉換匹配器
· 匹配器作為謂詞(Predicates)
· 匹配器作為測試斷言
· 動作(Actions)
· 返回一個值
· 副作用(Side Effects)
· 使用函數或函數對象作為動作(Action)
· 默認動作(Default Action)
· 復合動作(Composite Actions)
· 基數(Cardinalities)
· 序列(Sequences)
· 驗證并重置Mock
定義一個模擬(Mock)類
假設有
class Foo {
...
virtual ~Foo();
virtual int GetSize() const = 0;
virtual string Describe(const char* name) = 0;
virtual string Describe(int type) = 0;
virtual bool Process(Bar elem, int count) = 0;
};
(注意 ~Foo() 必須 是virtual的) 我們可以如下定義它的模擬類
#include <gmock/gmock.h>
class MockFoo : public Foo {
MOCK_CONST_METHOD0(GetSize, int());
MOCK_METHOD1(Describe, string(const char* name));
MOCK_METHOD1(Describe, string(int type));
MOCK_METHOD2(Process, bool(Bar elem, int count));
};
如下,建立一個"合適"的模擬對象 (mock object)來忽略所有不關心的調用,或者是一個"精確"的模擬對象讓這些調用置為失敗:
NiceMock<MockFoo> nice_foo; // The type is a subclass of MockFoo.
StrictMock<MockFoo> strict_foo; // The type is a subclass of MockFoo.
要模擬
template <typename Elem>
class StackInterface {
...
virtual ~StackInterface();
virtual int GetSize() const = 0;
virtual void Push(const Elem& x) = 0;
};
(注意 ~StackInterface() 必須 是 virtual 的) 只要追加_T 到 MOCK_* 宏即可:
template <typename Elem>
class MockStack : public StackInterface<Elem> {
...
MOCK_CONST_METHOD0_T(GetSize, int());
MOCK_METHOD1_T(Push, void(const Elem& x));
};
如果你模擬的函數不是使用默認的調用約定,你可以追加 _WITH_CALLTYPE 到前兩段講到的所有宏里,并把調用約定作為宏的第一個參數,例如,
MOCK_METHOD_1_WITH_CALLTYPE(STDMETHODCALLTYPE, Foo, bool(int n));
MOCK_CONST_METHOD2_WITH_CALLTYPE(
STDMETHODCALLTYPE, Bar, int(double x, double y));
這里的 STDMETHODCALLTYPE 是Windows中的頭文件 <objbase.h> 定義的.
在測試中使用模擬(Mocking)
典型的流程是:
1. 引入你要用到的 Google Mock 名稱. 除宏或其它特別提到的之外所有 Google Mock 名稱都位于testing 名空間之下.
2. 建立模擬對象(mock objects).
3. 可選的,設置模擬對象的默認動作.
4. 在模擬對象上設置你的預期(它們怎樣被調用,應該怎樣回應?).
5. 調用使用模擬對象的代碼,如有必要,使用Google Test斷言檢查返回值.
6. 當模擬對象析構后,Google Mock 自動驗證它是否達到所有預期.
這里是一個例子:
1. using testing::Return;
2.
3. TEST(BarTest, DoesThis) {
4. MockFoo foo;
5.
6. ON_CALL(foo, GetSize())
7. .WillByDefault(Return(1));
8.
9.
10. EXPECT_CALL(foo, Describe(5))
11. .Times(3)
12. .WillRepeatedly(Return("Category 5"));
13.
14.
15. EXPECT_EQ("good", MyProductionFunction(&foo));
16. }
設置默認動作
對于返回void, bool, 數值, 或指針的函數, Google Mock 都有一個內置默認動作.
要自定義返回類型為T的函數的默認動作:
1. using testing::DefaultValue;
2.
3. DefaultValue<T>::Set(value);
4.
5. DefaultValue<T>::Clear();
要自定義特定方法的默認動作,使用ON_CALL():
ON_CALL(mock_object, method(matchers))
.WithArguments(multi_argument_matcher) ?
.WillByDefault(action);
設置預期(Expectations)
EXPECT_CALL()在模擬方法(mock method)上設置預期(它們怎樣被調用?應該怎樣回應?):
EXPECT_CALL(mock_object, method(matchers))
.WithArguments(multi_argument_matcher) ?
.Times(cardinality) ?
.InSequence(sequences) *
.WillOnce(action) *
.WillRepeatedly(action) ?
.RetiresOnSaturation(); ?
如果沒有Times(), 那么cardinality就依據其它參數做出假定:
· Times(1) 既沒有WillOnce()也沒有WillRepeatedly();
· Times(n) 有n個WillOnce()但沒有WillRepeatedly(), 這里的n >= 1;
· Times(AtLeast(n)) 有n個WillOnce()并且有一個WillRepeatedly(), 這里的n >= 0.
沒用EXPECT_CALL()的方法可以自由地調用任意次, 并以默認動作應對每次調用.
匹配器(matcher)
匹配器 (matcher)匹配單個參數(指函數參數). 內置的匹配器分成幾種類型(下面的argument指函數參數):
_ |
argument& nbsp;可以是適當類型的任意值 |
A<type>() 或 An<type>() |
argument& nbsp;可以是type類型的任意值 |
Eq(value) 或 value |
argument == value |
Ge(value) |
argument >= value |
Gt(value) |
argument > value |
Le(value) |
argument <= value |
Lt(value) |
argument < value |
Ne(value) |
argument != value |
NULL |
argument 是 NULL. |
NotNull() |
argument 是一個非null指針. |
Ref(variable) |
argument 是一個variable的引用. |
TypedEq<type>(value) |
argument 是type類型并且等于value.當模擬函數被重載時你可能需要用它來代替Eq(value). |
除了Ref(), 這些匹配器在測試用例中使用一份value的拷貝來修改或析構. 如果編譯器提示value沒有public的拷貝構造, 可以嘗試使用ByRef()包裝, 比如. Eq(ByRef(non_copyable_value)). 如果你這樣做的話, 請確保調用之后non_copyable_value沒有被修改.
浮點數匹配器
DoubleEq(a_double) |
argument 是一個double值,近似等于a_double, 兩個NaN是不相等的. |
FloatEq(a_float) |
argument 是一個float值,近似等于a_float, 兩個NaN是不相等的. |
NanSensitiveDoubleEq(a_double) |
argument 是一個double值,近似等于a_double, 兩個NaN是相等的. |
NanSensitiveFloatEq(a_float) |
argument 是一個float值,近似等于a_float, 兩個NaN是相等的. |
這些匹配器使用基于ULP的比較 (和Google Test所使用的一樣). 它們基于預期的絕對值自動選擇一個合理的誤差范圍. DoubleEq()和FloatEq()符合IEEE標準, 該標準要求比較兩個NaN是否相等時返回false. NanSensitive* 版本則視兩個NaN為相等, 這是用戶通常所希望的.
argument可以是C風格字符串或C++的string對象:
ContainsRegex(string) |
argument 匹配給定的正則表達式. |
EndsWith(suffix) |
argument 含有后綴suffix. |
HasSubstr(string) |
argument 含有子串string. |
MatchesRegex(string) |
argument 從第一個字符到最后一個字符都完全匹配給定的正則表達式. |
StartsWith(prefix) |
argument 含有前綴prefix. |
StrCaseEq(string) |
argument 與string相等, 忽略大小寫. |
StrCaseNe(string) |
argument 與string不等, 忽略大小寫. |
StrEq(string) |
argument 與string相等. |
StrNe(string) |
argument 與string不等. |
StrCaseEq(), StrCaseNe(), StrEq(), 和 StrNe() 也能工作于寬字符字符串.
很多STL-風格容器支持==, 所以你可以使用Eq(expected_container)或簡單地expected_container來精確匹配容器. 如果你想直接寫元素或更靈活地匹配, 可以使用:
ElementsAre(e0, e1, ..., en) |
argument 有n + 1個元素, 每i個元素匹配ei, 它們可以是一個值或是一個匹配器. 允許0到10個參數. |
ElementsAreArray(array) 或 ElementsAreArray(array, count) |
和ElementsAre()相同, 除了預期值/匹配器來源于一個C風格數組. |
Field(&class::field, m) |
argument.field (或 argument->field, 當argument是一個指針時)與匹配器m匹配, 這里的argument是一個class類的實例. |
Property(&class::property, m) |
argument.property() (或 argument->property(), 當argument是一個指針時)與匹配器m匹配, 這里的argument是一個class類的實例. |
ResultOf(f, m) |
f(argument) 與匹配器m匹配, 這里的f是一個函數或函數對象. |
Pointee(m) |
argument (不論是智能指針還是原始指針) 指向的值與匹配器m匹配. |
Truly(predicate) |
predicate(argument) 返回值為true, 這里的predicate是一個函數或函數對象. |
多參數匹配器
這些匹配器為元組類型(tuple types). 它們可以用在.WithArguments()里匹配兩個參數的函數:
Eq() |
arg1 == arg2 |
Ge() |
arg1 >= arg2 |
Gt() |
arg1 > arg2 |
Le() |
arg1 <= arg2 |
Lt() |
arg1 < arg2 |
Ne() |
arg1 != arg2 |
復合匹配器
你可以把一個或多個匹配器合成一個匹配器:
AllOf(m1, m2, ..., mn) |
argument 匹配所有的匹配器m1到mn. |
AnyOf(m1, m2, ..., mn) |
argument 至少匹配m1到mn中的一個. |
Not(m) |
argument 不與匹配器m匹配. |
MatcherCast<T>(m) |
轉換匹配器m為Matcher<T>類型. |
通過Matches(m),Google Mock 能讓你轉換一個匹配器m到一個無參數謂詞(就象STL算法使用的一樣).
ASSERT_THAT(expression, m) |
如果expression的值和匹配器m不匹配,就產生一個致命失敗. |
EXPECT_THAT(expression, m) |
如果expression的值和匹配器m不匹配,就產生一個非致命失敗. |
動作(Actions)
動作 (Actions)指定了一個模擬函數被調用時應該做什么.
Return() |
從一個void的模擬函數中返回 |
Return(value) |
返回value. |
ReturnNull() |
返回NULL指針. |
ReturnRef(variable) |
返回variable的引用. |
Assign(&variable, value) |
給variable 賦值value. |
SetArgumentPointee<N>(value) |
給第N(0-based)個參數指向的變量賦值value. |
SetArrayArgument<N>(first, last) |
拷貝源范圍[first, last)里的元素到第N(0- based)個參數指向的數組, 它可以是一個指針或一個迭代器. 這個動作不會改變源范圍元素的所有權. |
SetErrnoAndReturn(error, value) |
設置errno為error,并且返回value. |
Invoke(f) |
使用模擬函數的參數調用f, 這里的f可以是全局/靜態(tài)函數或函數對象. |
Invoke(object_pointer, &class::method) |
使用模擬函數的參數調用object_pointer對象的mothod方法. |
InvokeWithoutArgs(f) |
無參數調用f, 這里的f可以是全局/靜態(tài)函數或函數對象. |
InvokeWithoutArgs(object_pointer, &class::method) |
無參數調用object_pointer對象的mothod方法. |
InvokeArgument<N>(arg1, arg2, ..., argk) |
調用模擬函數的第N個(0-based)參數, 這個參數必須是一個函數或函數對象, 傳入這里的k個參數. |
這里調用函數的返回值將作為動作的返回值.
當定義一個函數或函數對象用于Invoke*(), 你可以使用Unused來聲明一些不用的參數:
double Distance(Unused, double x, double y) { return sqrt(x*x + y*y); }
...
EXPECT_CALL(mock, Foo("Hi", _, _)).WillOnce(Invoke(Distance));
在 InvokeArgument<N>(...), 如果一個參數需要引用傳遞, 可以使用ByRef()包裝. 例如,
InvokeArgument<2>(5, string("Hi"), ByRef(foo))
調用模擬函數的第二個參數, 以傳值的方式傳入5和string("Hi"), 引用的方式傳入foo.
DoDefault() |
使用默認動作(由ON_CALL()指定或內置). |
注意: 由于技術原因, DoDefault()不能用于復合動作 - 嘗試的結果是一個運行期錯誤.
DoAll(a1, a2, ..., an) |
每次發(fā)動時執(zhí)行a1到an的所有動作. |
IgnoreResult(a) |
執(zhí)行動作a并忽略它的返回值. a不能返回void. |
WithArg<N>(a) |
傳入模擬函數的第N個(0-based)參數作為動作a的參數并執(zhí)行之. |
WithArgs<N1, N2, ..., Nk>(a) |
傳入選中的模擬函數的多個第N個(0-based)參數作為動作a的參數并執(zhí)行之. |
WithoutArgs(a) |
無參數執(zhí)行動作a. |
基數(Cardinalities)
基數用于Times()中來指定模擬函數將被調用多少次:
AnyNumber() |
函數可以被調用任意次. |
AtLeast(n) |
預計至少調用n次. |
AtMost(n) |
預計至多調用n次. |
Between(m, n) |
預計調用次數在m和n(包括n)之間. |
Exactly(n) 或 n |
預計精確調用n次. 特別是, 當n為0時,函數應該永遠不被調用. |
序列(Sequences)
序列 (Sequences) 指定預期的順序. 在同一序列里的所有預期調用必須按它們指定的順序發(fā)生; 反之則可以是任意順序.
建立序列:
Sequence s1, s2;
使用序列:
EXPECT_CALL(foo, Reset())
.InSequence(s1, s2)
.WillOnce(Return(true));
EXPECT_CALL(foo, GetSize())
.InSequence(s1)
.WillOnce(Return(1));
EXPECT_CALL(foo, Describe(A<const char*>()))
.InSequence(s2)
.WillOnce(Return("dummy"));
(Reset()必須在GetSize()和Describe()之前被調用; 之后兩個則可以任意順序.)
方便地在一個序列中放入多個預期:
{
InSequence dummy;
EXPECT_CALL(...)...;
EXPECT_CALL(...)...;
...
EXPECT_CALL(...)...;
}
(在dummy的生命周期內的所有預期的調用必須以精確的順序發(fā)生. 至于名字dummy與此無關.)
驗證并重置 Mock
當模板對象析構時Google Mock將驗證其上的所有預期, 或者你也可以提前做這些事:
1. using testing::Mock;
2. ...
3.
4.
5. Mock::VerifyAndClearExpectations(&mock_obj);
6. ...
7.
8.
9.
10. Mock::VerifyAndClear(&mock_obj);