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可以是全局/静态函数或函数对象. |
Invoke(object_pointer, &class::method) |
使用模拟函数的参数调用object_pointer对象的mothod方法. |
InvokeWithoutArgs(f) |
无参数调用f, 这里的f可以是全局/静态函数或函数对象. |
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) |
每次发动时执行a1到an的所有动作. |
IgnoreResult(a) |
执行动作a并忽略它的返回值. a不能返回void. |
WithArg<N>(a) |
传入模拟函数的第N个(0-based)参数作为动作a的参数并执行之. |
WithArgs<N1, N2, ..., Nk>(a) |
传入选中的模拟函数的多个第N个(0-based)参数作为动作a的参数并执行之. |
WithoutArgs(a) |
无参数执行动作a. |
基数(Cardinalities)
基数用于Times()中来指定模拟函数将被调用多少次:
AnyNumber() |
函数可以被调用任意次. |
AtLeast(n) |
预计至少调用n次. |
AtMost(n) |
预计至多调用n次. |
Between(m, n) |
预计调用次数在m和n(包括n)之间. |
Exactly(n) 或 n |
预计精确调用n次. 特别是, 当n为0时,函数应该永远不被调用. |
序列(Sequences)
序列 (Sequences) 指定预期的顺序. 在同一序列里的所有预期调用必须按它们指定的顺序发生; 反之则可以是任意顺序.
建立序列:
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的生命周期内的所有预期的调用必须以精确的顺序发生. 至于名字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);