woaidongmao

文章均收录自他人博客,但不喜标题前加-[转贴],因其丑陋,见谅!~
随笔 - 1469, 文章 - 0, 评论 - 661, 引用 - 0
数据加载中……

[译]Google C++ Mocking Framework Cheat Sheet

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)

模拟(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> 定义的.

在测试中使用模拟(Mocking)

典型的流程是:

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);

设置预期(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) nWillOnce()但没有WillRepeatedly(), 这里的n >= 1;

·                     Times(AtLeast(n)) nWillOnce()并且有一个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, 它们可以是一个值或是一个匹配器. 允许010个参数.

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 匹配所有的匹配器m1mn.

AnyOf(m1, m2, ..., mn)

argument 至少匹配m1mn中的一个.

Not(m)

argument 不与匹配器m匹配.

转换匹配器

MatcherCast<T>(m)

转换匹配器mMatcher<T>类型.

匹配器作为谓词 (Predicates)

通过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的引用.

副作用(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)

设置errnoerror,并且返回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))

调用模拟函数的第二个参数, 以传值的方式传入5string("Hi"), 引用的方式传入foo.

默认动作(Default Actions)

DoDefault()

使用默认动作(ON_CALL()指定或内置).

注意: 由于技术原因, DoDefault()不能用于复合动作 - 尝试的结果是一个运行期错误.

复合动作(Composite Actions)

DoAll(a1, a2, ..., an)

每次发动时执行a1an的所有动作.

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)

预计调用次数在mn(包括n)之间.

Exactly(n) n

预计精确调用n. 特别是, n0,函数应该永远不被调用.

序列(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.                // mock_obj进行验证并移除所有预期;

4.                // 如果成功返回true.

5.                Mock::VerifyAndClearExpectations(&mock_obj);

6.                ...

7.                // mock_obj进行验证并移除所有预期;

8.                // 同时也移除ON_CALL()设置的默认动作;

9.                // 如果成功返回true.

10.            Mock::VerifyAndClear(&mock_obj);

 

posted on 2009-05-02 01:52 肥仔 阅读(1778) 评论(0)  编辑 收藏 引用 所属分类: 库 & 代码段


只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理