注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

Koala++'s blog

计算广告学 RTB

 
 
 

日志

 
 

Google Mock进阶篇 [10] (Google Mock Cookbook译文)  

2012-05-12 17:56:32|  分类: C++ |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |

Writing New Actions Quickly

如果内置的动作不适合你,并且发现用Invoke()很不方便,你可以用ACTION*宏系列中的宏来快速定义一个新动作,它可以像内置的动作一样用于你的代码中。

通过在命名空间中写:

ACTION(name) { statements; }

你可以定义一个执行statements名为name的动作。Statements返回的值会作为action返回的值。在statements中,你可以通过argK来引用Mock函数的第K个参数(0开始)。比如:

ACTION(IncrementArg1) { return ++(*arg1); }

允许你写:

... WillOnce(IncrementArg1());

注意,你不需要指定Mock函数参数的类型,另外你要保证你的代码是类型安全的:如果*arg1不支持++运算符,或是如果++(*arg1)Mock函数的返回值类型不兼容,你会得到一个编译错误。

另一个例子:

ACTION(Foo) {

  (*arg2)(5);

  Blah();

  *arg1 = 0;

  return arg0;

}

定义一个动作Foo(),它会传入5调用第二个参数(一个函数指针),调用函数Blah(),设置第一个参数指针向指向的值为0,返回第零个参数。

以了更方便更灵活,你可以在ACTION中用下面预定义的符号:

argK_type

Mock函数第K(0开始0)参数的类型

args

Mock函数所有参数组成的一个元组

args_type

Mock函数所有参数类型组成的一个元组

return_type

Mock函数的返回类型

function_type

Mock函数的类型

比如,用ACTIONMOCK函数作为一个stub动作:

int DoSomething(bool flag, int* ptr);

我们有:

Pre-defined Symbol

Is Bound To

arg0

flag的值

arg0_type

flag的类型bool

arg1

ptr的值

arg1_type

ptr的类型int*

args

参数元组(flag ptr)

args_type

参数元组std::tr1::tuple<bool, int*>

return_type

返回类型int

function_type

函数类型int(bool, int*)

Writing New Parameterized Actions Quickly

有时你想参数化你定义的一个动作。对此我们有另一个宏:

ACTION_P(name, param) { statements; }

比如:

ACTION_P(Add, n) { return arg0 + n; }

允许你写:

// Returns argument #0 + 5.

... WillOnce(Add(5));

了为方便起来,我们用术语arguments表示 用于调用Mock函数的值,用术语parameters表示实例化动作的值。

现在你同样不需要提供参数(parameter)的类型。假设参数的名称为param,你可以用Google Mock定义的符号param_type来表示parameter的类型,它是由编译器推出的。比如在上面的ACTION_P(Add, n),你可以用n_type来表示n的类型。

Google Mock要同样提供ACTION_P2ACTION_P3,等等来支持多参数(multi-parameter)动作,比如:

ACTION_P2(ReturnDistanceTo, x, y) {

  double dx = arg0 - x;

  double dy = arg1 - y;

  return sqrt(dx*dx + dy*dy);

}

 

可以让你写:

... WillOnce(ReturnDistanceTo(5.0, 26.5));

你可以视ACTION为一个退化的参数化动作,它的参数个数为0.

你同样可以很容易的定义重载不同参数个数的动作。

ACTION_P(Plus, a) { ... }

ACTION_P2(Plus, a, b) { ... }

Restricting the Type of an Argument or Parameter in an ACTION

为了最大化简洁性和可重用性,ACTION*宏不用你提供MOCK函数arguments和动作parameters。相反,我们让编译器帮我们推导出类型。

但有时,我们想让类型更准确一些,有几个技巧可以做到这点。比如:

ACTION(Foo) {

   // Makes sure arg0 can be converted to int.

   int n = arg0;

   ... use n instead of arg0 here ...

}

 

ACTION_P(Bar, param) {

   // Makes sure the type of arg1 is const char*.

   ::testing::StaticAssertTypeEq<const char*, arg1_type>();

  

   // Makes sure param can be converted to bool.

   bool flag = param;

}

其中StaticAssertTypeEqGoogle Test中一个编译期判断两个类型是否匹配的断言。

Writing New Action Templates Quickly

有时你想给一个动作明确的模板参数,而不是由编译器推导出参数类型。ACTION_TEMPLATE()支持这个功能,它可以视为是ACTION()ACTION_P*()的一个扩展。

语法:

ACTION_TEMPLATE(ActionName,

   HAS_m_TEMPLATE_PARAMS(kind1, name1, ..., kind_m, name_m),

   AND_n_VALUE_PARAMS(p1, ..., p_n)) { statements; }

上面定义了一个动作模板,它接受m个明确的模板参数和n个参数,其中m取值在110n取值在010name_i是第i个模板参数的名字,kind_i表明它是否是一个typename,或是一个整形常量,或是一个模板。P_i是第i个参数值的名字。

比如:

// DuplicateArg<k, T>(output) converts the k-th argument of the mock

// function to type T and copies it to *output.

ACTION_TEMPLATE(DuplicateArg,

                // Note the comma between int and k:

                HAS_2_TEMPLATE_PARAMS(int, k, typename, T),

                AND_1_VALUE_PARAMS(output)) {

  *output = T(std::tr1::get<k>(args));

}

创建一个动作模板的实现,可以写:

ActionName<t1, ..., t_m>(v1, ..., v_n)

其中ts是模板参数,vs是参数值。参数类型由编译器推出。比如:

using ::testing::_;

...

int n;

EXPECT_CALL(mock, Foo(_, _))

  .WillOnce(DuplicateArg<1, unsigned char>(&n));

如果你想明确地指明参数类型,你可以提供更多的模板参数:

ActionName<t1, ..., t_m, u1, ..., u_k>(v1, ..., v_n)

其中u_iv_i参数所期望的类型。

ACTION_TEMPLATEACTION/ACTION_P*可以通过参数个数重载,但不能通过模板参数个数重载。如果没有这个限制,下面的代码含义就不明确了:

OverloadedAction<int, bool>(x);

我们是在用一个模板参数的功能,其中bool是指x的类型呢?或是两个板板参数,其中编译器需要推出x的类型。

Using the ACTION Object’s Type

如果你写一个返回一个ACTION对象的函数,你将需要知道它的类型,类型依赖于用于定义动作和参数类型的达能。规则是很简单的:

Given Definition

Expression

Has Type

ACTION(Foo)

Foo()

FooAction

ACTION_TEMPLATE(Foo, HAS_m_TEMPLATE_PARAMS(...), AND_0_VALUE_PARAMS())

Foo<t1, ..., t_m>()

FooAction<t1, ..., t_m>

ACTION_P(Bar, param)

Bar(int_value)

BarActionP<int>

ACTION_TEMPLATE(Bar, HAS_m_TEMPLATE_PARAMS(...), AND_1_VALUE_PARAMS(p1))

Bar<t1, ..., t_m>(int_value)

FooActionP<t1, ..., t_m, int>

ACTION_P2(Baz, p1, p2)

Baz(bool_value, int_value)

BazActionP2<bool, int>

ACTION_TEMPLATE(Baz, HAS_m_TEMPLATE_PARAMS(...), AND_2_VALUE_PARAMS(p1, p2))

Baz<t1, ..., t_m>(bool_value, int_value)

FooActionP2<t1, ..., t_m, bool, int>

...

...

...

注意,我们要选择不同的前缀(ActionActionPActionP2,等等)用于区别有不同参数个数的动作,否则不能通过参数个数重载的动作。

Writing New Monomorphic Actions

虽然ACTION*宏很方便,但有时它们是不合适的。比如,在前一节中介绍的技巧,不能让你直接指定Mock函数参数和动作参数,这通常会引发一些没有优化的错误信息,这又会困扰一些不熟悉此的用户。实现根据参数类型重载动作,需要越过重重障碍。

另一个方法是实现::testing::ActionInterface<F>,其中F是用于动作的Mock函数的类型。比如:

template <typename F>class ActionInterface {

public:

   virtual ~ActionInterface();

  

   // Performs the action.  Result is the return type of function type

   // F, and ArgumentTuple is the tuple of arguments of F.

   //

   // For example, if F is int(bool, const string&), then Result would

   // be int, and ArgumentTuple would be tr1::tuple<bool, const string&>.

   virtual Result Perform(const ArgumentTuple& args) = 0;

};

 

using ::testing::_;

using ::testing::Action;

using ::testing::ActionInterface;

using ::testing::MakeAction;

 

typedef int IncrementMethod(int*);

 

class IncrementArgumentAction : public ActionInterface<IncrementMethod> {

public:

   virtual int Perform(const tr1::tuple<int*>& args) {

   int* p = tr1::get<0>(args);  // Grabs the first argument.

   return *p++;

   }

};

 

Action<IncrementMethod> IncrementArgument() {

   return MakeAction(new IncrementArgumentAction);

}

...

 

EXPECT_CALL(foo, Baz(_))

  .WillOnce(IncrementArgument());

 

int n = 5;

foo.Baz(&n);  // Should return 5 and change n to 6.

Writing New Polymorphic Actions

上一节中介绍了如何定义自己的动作。这是不错,除了你需要知道动作中要用的函数类型这一点。有时这会是一个问题。比如,如果你想用在动作中用不同类型的函数(比如Return()SetArgPointee())

如果一个动作可以用在不同类型的Mock函数中,我们就称它是多形的。MakePolymorphicActions()函数模板让这种定义很容易。

namespace testing {

 

template <typename Impl>

   PolymorphicAction<Impl> MakePolymorphicAction(const Impl& impl);

 

}  // namespace testing

举一例子,我们定义一个返回Mock函数参数列表中第二个参数的动作。第一步是定义一个实现类:

class ReturnSecondArgumentAction {

public:

   template <typename Result, typename ArgumentTuple>

   Result Perform(const ArgumentTuple& args) const {

     // To get the i-th (0-based) argument, use tr1::get<i>(args).

     return tr1::get<1>(args);

   }

};

实现类不需要继承任何特殊的类。重要的是它必须有一个Perform()模板函数。这个函数模板将Mock函数的参数视为一个元组参数,并返回动作的结果。它可以是const的,也可以不是,但它必须有且仅有一个模板参数,它是结果类型。另句话说,你必须可以调用Perform<R>(args),其中RMock函数的返回类型,args是它的参数以元组形式的表示。

接下来,我们用MakePolymorphicAction()将这个实现类对象变为我们所需的多形动作。它封装成下面这种形式会很方便:

using ::testing::MakePolymorphicAction;

using ::testing::PolymorphicAction;

 

PolymorphicAction<ReturnSecondArgumentAction> ReturnSecondArgument() {

   return MakePolymorphicAction(ReturnSecondArgumentAction());

}

现在,你可以像用内置动作一样的方式来用这个多形的动作:

using ::testing::_;

 

class MockFoo : public Foo {

public:

   MOCK_METHOD2(DoThis, int(bool flag, int n));

   MOCK_METHOD3(DoThat, string(int x, const char* str1, const char* str2));

};

...

 

MockFoo foo;

EXPECT_CALL(foo, DoThis(_, _))

   .WillOnce(ReturnSecondArgument());

EXPECT_CALL(foo, DoThat(_, _, _))

   .WillOnce(ReturnSecondArgument());

...

foo.DoThis(true, 5);         // Will return 5.

foo.DoThat(1, "Hi", "Bye");  // Will return "Hi".

Teaching Google Mock How to Print Your Values

当一个未设置或是未期望的调用发生时,Google Mock会打印参数值和栈trace帮你debug。像EXPECT_THATEXPECT_EQ这些断言宏会在断言失败时打印这些值。Google MockGoogle Test会用Google Test的用户可扩展值打印器。

这个打印器知道如何打印C++内置类型,普通数据,STL容器,和支持<<操作符的任何类型。其它类型,它会以值的原始字符的形式打印,希望可以帮助到你。Google Test的高级指南中解释了如何扩展打印器来打印你自己的类型。

  评论这张
 
阅读(2148)| 评论(0)
推荐 转载

历史上的今天

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2017