Google C++ Mocking Framework Cheat Sheet中文版
模拟(Mocking)普通类
假设有
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.
模拟(Mocking)模板类
要模拟
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));
};
为模拟函数(Mock Functions)指定调用约定
如果你模拟的函数不是使用默认的调用约定,你可以追加 _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> 定义的.
典型的流程是:
1. 引入你要用到的 Google Mock 名称. 除宏或其它特别提到的之外所有 Google Mock 名称都位于testing 名空间之下.
2. 建立模拟对象(mock objects).
3. 可选的,设置模拟对象的默认动作.
4. 在模拟对象上设置你的预期(它们怎样被调用,应该怎样回应?).
5. 调用使用模拟对象的代码,如有必要,使用Google Test断言检查返回值.
6. 当模拟对象析构后,Google Mock 自动验证它是否达到所有预期.
这里是一个例子:
1. using testing::Return; // #1
2.
3. TEST(BarTest, DoesThis) {
4. MockFoo foo; // #2
5.
6. ON_CALL(foo, GetSize()) // #3
7. .WillByDefault(Return(1));
8. // ... 其它默认动作 ...
9.
10. EXPECT_CALL(foo, Describe(5)) // #4
11. .Times(3)
12. .WillRepeatedly(Return("Category 5"));
13. // ... other expectations ...
14.
15. EXPECT_EQ("good", MyProductionFunction(&foo)); // #5
16. } // #6
对于返回void, bool, 数值, 或指针的函数, Google Mock 都有一个内置默认动作.
要自定义返回类型为T的函数的默认动作:
1. using testing::DefaultValue;
2.
3. DefaultValue<T>::Set(value); // 设置默认的返回值
4. // ... use the mocks ...
5. DefaultValue<T>::Clear(); // 重置默认值
要自定义特定方法的默认动作,使用ON_CALL():
ON_CALL(mock_object, method(matchers))
.WithArguments(multi_argument_matcher) ?
.WillByDefault(action);
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)匹配单个参数(指函数参数). 内置的匹配器分成几种类型(下面的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>类型. |
匹配器作为谓词 (Predicates)
通过Matches(m),Google Mock 能让你转换一个匹配器m到一个无参数谓词(就象STL算法使用的一样).
匹配器作为测试断言
ASSERT_THAT(expression, m) | 如果expression的值和匹配器m不匹配,就产生一个致命失败. |
EXPECT_THAT(expression, m) | 如果expression的值和匹配器m不匹配,就产生一个非致命失败. |
动作 (Actions)指定了一个模拟函数被调用时应该做什么.
返回一个值
Return() | 从一个void的模拟函数中返回 |
Return(value) | 返回value. |
ReturnNull() | 返回NULL指针. |
ReturnRef(variable) | 返回variable的引用. |
副作用(Side Effects)
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. |
使用函数或函数对象作为动作(Actions)
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.
默认动作(Default Actions)
DoDefault() | 使用默认动作(由ON_CALL()指定或内置). |
注意: 由于技术原因, DoDefault()不能用于复合动作 - 尝试的结果是一个运行期错误.
复合动作(Composite Actions)
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. |
基数用于Times()中来指定模拟函数将被调用多少次:
AnyNumber() | 函数可以被调用任意次. |
AtLeast(n) | 预计至少调用n次. |
AtMost(n) | 预计至多调用n次. |
Between(m, n) | 预计调用次数在m和n(包括n)之间. |
Exactly(n) 或 n | 预计精确调用n次. 特别是, 当n为0时,函数应该永远不被调用. |
序列 (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与此无关.)
当模板对象析构时Google Mock将验证其上的所有预期, 或者你也可以提前做这些事:
1. using testing::Mock;
2. ...
3. // 对mock_obj进行验证并移除所有预期;
4. // 如果成功返回true.
5. Mock::VerifyAndClearExpectations(&mock_obj);
6. ...
7. // 对mock_obj进行验证并移除所有预期;
8. // 同时也移除ON_CALL()设置的默认动作;
9. // 如果成功返回true.
10. Mock::VerifyAndClear(&mock_obj);