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

Koala++'s blog

计算广告学 RTB

 
 
 

日志

 
 

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

2012-05-12 08:57:55|  分类: C++ |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |

Extending Google Mock

Writing New Matchers Quickly

    MATCHER*宏系列可以很容易地用来定义自己的匹配器。语法是:

MATCHER(name, description_string_expression) { statements; }

    这个宏会定义一个名为name的匹配器,这个匹配器执行statements语句,statetements必须返回一个bool值,以来表示这次匹配是否成功。在statements内部,你可以用arg来表示被匹配的值,这个值的类型用arg_type表示。

    Description_string是一个字符串,用来描述这个匹配器的行为,并且在匹配失败的时候产生失败信息。它能(并且应该)对非逻辑进行判断( ::test::Not ),产生对应的错误描述。

    为了方便起见,我们允许描述字符串为空,在这种情况下Google Mock会用匹配器的名字中的单词作为描述。

    比如:

MATCHER(IsDivisibleBy7, "") { return (arg % 7) == 0; }

    允许你写:

// Expects mock_foo.Bar(n) to be called where n is divisible by 7.

EXPECT_CALL(mock_foo, Bar(IsDivisibleBy7()));

    或是:

using ::testing::Not;

...

EXPECT_THAT(some_expression, IsDivisibleBy7());

EXPECT_THAT(some_other_expression, Not(IsDivisibleBy7()));

当上面的断言失败时,它们会打印下面的信息:

Value of: some_expression

Expected: is divisible by 7

Actual: 27

...

Value of: some_other_expression

Expected: not (is divisible by 7)

Actual: 21

    其中描述”is divisible by 7””not (is divisiable by 7)”是通过IsDivisibleBy7这个匹配器名字自动产生的。

    正如你所注意到的,自动产生的描述(特别是由非逻辑产生的)并不是那么好。你可以自定义描述。

MATCHER(IsDivisibleBy7, std::string(negation ? "isn't" : "is") +

                   " divisible by 7") {

  return (arg % 7) == 0;

}

    或者,你可以将更多的信息用一个隐藏变量result_listener输出,来解释匹配结果。比如,一个更好的IsDivisibleBy7的更好定义是:

MATCHER(IsDivisibleBy7, "") {

   if ((arg % 7) == 0)

      return true;

 

   *result_listener << "the remainder is " << (arg % 7);

   return false;

}

    有了这个定义,上面断言会给出一个更好的提示:

Value of: some_expression

Expected: is divisible by 7

Actual: 27 (the remainder is 6)

    你应该让MatchAndExplain()打印其它附加信息,这些信息可以帮助一个用户理解匹配结果。注意它可以在成功的情况下解释为什么匹配成功( 除非它是显然的 )-这在Not内部的匹配器中是很有效的。没有必要打印参数本身,因为Google Mock已经为你打印了。

注意:

1.  所匹配的值的类型(arg_type)是由你使用匹配器的上下文决定的,它是由编译器提供给你的,所以你不用操心如何去定义它(你也没法定义)。这样允许匹配器是多形的(即支持多种类型的)。比如,IsDivisibleBy7()可以用于匹配任何支持arg % 7 == 0转换为bool的类型。在上面的Bar(IsDivisibleBy7())例子中,如果Bar()接受int参数,arg_type就是int,如果它接受unsigned long整形,arg_type就是unsigned long,等等。

2.  Google Mock不保证匹配器何时和被调用多少次。所以匹配器的逻辑必须是纯功能性的( 比如,它不能有任何副作用,并且结果也不能依赖于匹配的值和匹配器参数之外的东西 )。无论你如何定义,这个条件是你在定义一个匹配器时必须要满足的 ( 比如,用下面章节介绍的方法 )。特别是,一个匹配器决不能调用一个Mock函数,因为这会改变Mock对象和Google Mock的状态

Writing New Parameterized Matchers Quickly

有时你想定义有一个有参数的匹配器。对于这个要求你可以使用宏:

MATCHER_P(name, param_name, description_string) { statements; }

其中description_string可以是””或是引用了param_name的描述。

比如:

MATCHER_P(HasAbsoluteValue, value, "") { return abs(arg) == value; }

你可以写:

 EXPECT_THAT(Blah("a"), HasAbsoluteValue(n));

这会得到下面的信息(假设n10)

Value of: Blah("a")

Expected: has absolute value 10

Actual: -9

注意匹配器的描述和它的参数都被打印了,使信息更友好。

在匹配器定义内,你可以写foo_type来引用一个名为foo的参数类型。比如在上例MATCHER_P(HasAbsoluteValue, Value)中,你可以用value_type来引用value的类型。

Google Mock还提供MATCHER_P2MATCHER_P3...MATCHER_P10以来支持多参数的匹配器:

MATCHER_Pk(name, param_1, ..., param_k, description_string) { statements; }

请注意匹配器的提示信息是针对匹配器一个特定的实例的,即参数是与真实值相关的。所以通常你会想要参数值成为描述的一部分。Google Mock可以让你通过在描述字符串中引用匹配器参数来达到这个目的。

      比如:

using ::testing::PrintToString;

MATCHER_P2(InClosedRange, low, hi,

       std::string(negation ? "isn't" : "is") + " in range [" +

       PrintToString(low) + ", " + PrintToString(hi) + "]") {

   return low <= arg && arg <= hi;

}

...

EXPECT_THAT(3, InClosedRange(4, 6));

会产生如下的失败信息:

Expected: is in range [4, 6]

如果你用””作为描述信息,失败信息中会包含匹配器的名字,后面跟着以元组形式打印的参数值。比如:

MATCHER_P2(InClosedRange, low, hi, "") { ... }

...

EXPECT_THAT(3, InClosedRange(4, 6));

会产生一个如下的失败信息:

Expected: in closed range (4, 6)

出于输入的方便,你可以视

MATCHER_Pk(Foo, p1, ..., pk, description_string) { ... }

为下面的简写:

template <typename p1_type, ..., typename pk_type>

FooMatcherPk<p1_type, ..., pk_type>

Foo(p1_type p1, ..., pk_type pk) { ... }

    当你写Foo(v1, ..., vk), 编译器会自己推出v1, ..., vk参数的类型。如果你推出的类型结果不满意,你可以指定模板参数类型,比如Foo<long, bool>(5, false)。如前面所提到的,你不需要去指定arg_type,因为它是由所用匹配器的上下文所决定的。

    你可以将Foo(p1, ..., pk)的结果赋给FooMatcherPk<p1_typem, ..., pk_type>类型的变量。这在组合匹配器时会比较有用。无参的匹配器或只有一个参数的匹配器有特殊的类型:你可以将Foo()赋值给一个FooMatcher类型变量,将Foo(p)赋值给一个FooMatcher<p_type>类型变量。

    尽管你可以用引用类型来实例化匹配器模板,然而用指针传递参数通常会使你的代码更可读。但是如果你还是想通过引用传递参数,注意由匹配器产生的失败信息中的值是对象引用的值,而不是它的地址。

    你可以重载不同参数个数的匹配器。

MATCHER_P(Blah, a, description_string_1) { ... }

MATCHER_P2(Blah, a, b, description_string_2) { ... }

    虽然总是用MATCHER*来定义一个新的匹配器是很有吸引力的,但你也应该考虑用MatcherInterface或是用MakePolymorphicMatcher()来定义(下面会介绍),特别是你会经常用这个匹配器的时候。尽管这些方法会花费更多的力气,但它们会给你更多的控制能力:可以控制匹配值的类型,匹配器参数,这样一般也会产生更好的编译提示信息,用这些方法以长远的目光来看是更好的选择。它们还允许重载不同参数类型(而不是仅能通过不同参数个数重载)

Wrting New Monomorphic Matchers

一个实现了::testing::MatcherInterface<T>T参数类型的匹配器可以做两种事:它判断参数T是否匹配匹配器,并可以描述它所匹配的类型。后一种能力可以在期望失败时给出可读的错误信息。

class MatchResultListener {

 public:

  ...

  // Streams x to the underlying ostream; does nothing if the ostream

  // is NULL.

  template <typename T>

  MatchResultListener& operator<<(const T& x);

 

  // Returns the underlying ostream.

  ::std::ostream* stream();

};

 

template <typename T>

class MatcherInterface {

 public:

  virtual ~MatcherInterface();

 

  // Returns true iff the matcher matches x; also explains the match

  // result to 'listener'.

  virtual bool MatchAndExplain(T x, MatchResultListener* listener) const = 0;

 

  // Describes this matcher to an ostream.

  virtual void DescribeTo(::std::ostream* os) const = 0;

 

  // Describes the negation of this matcher to an ostream.

  virtual void DescribeNegationTo(::std::ostream* os) const;

};

    如果你需要一个自定的Matcher,但Truly不是一个好选择(比如,你也不会对Truly(predicate)的提示信息不满意,或是你想让你的匹配器是多形的(接受多种类型),如Eq(value)一样),你可以定义通过两步来定义你想的任何匹配器:第一步定义匹配器接口,第二步定义创建一个匹配器实例的工厂函数。第二步不是必须的,但它会使使用匹配器的语法更优雅。

    比如,你可以定义一个匹配器来判断一个int值是否可以被7整除,然后使用它:

using ::testing::MakeMatcher;

using ::testing::Matcher;

using ::testing::MatcherInterface;

using ::testing::MatchResultListener;

 

class DivisibleBy7Matcher : public MatcherInterface<int> {

 public:

  virtual bool MatchAndExplain(int n, MatchResultListener* listener) const {

    return (n % 7) == 0;

  }

 

  virtual void DescribeTo(::std::ostream* os) const {

    *os << "is divisible by 7";

  }

 

  virtual void DescribeNegationTo(::std::ostream* os) const {

    *os << "is not divisible by 7";

  }

};

 

inline Matcher<int> DivisibleBy7() {

  return MakeMatcher(new DivisibleBy7Matcher);

}

...

 

EXPECT_CALL(foo, Bar(DivisibleBy7()));

你可以通过输出更多的信息到MatchAndExplan()函数中的listener变量来改进提示信息:

class DivisibleBy7Matcher : public MatcherInterface<int> {

 public:

  virtual bool MatchAndExplain(int n,

                               MatchResultListener* listener) const {

    const int remainder = n % 7;

    if (remainder != 0) {

      *listener << "the remainder is " << remainder;

    }

    return remainder == 0;

  }

  ...

};

然后,EXPECT_THAT(x, DivisiableBy7())可能会产生如下的信息:

Value of: x

Expected: is divisible by 7

Actual: 23 (the remainder is 2)

Writing New Polymorphic Matchers

在前一节中的你了解了如何去写自己的匹配器。只有还有一个问题:一个用MakeMatcher()创建的匹配器只能在参数类型确定的情况下用。如果你想要一个多形的(接受多种类型)匹配器(比如,Eq(x)可以匹配任何value==xvaluevaluex不一定是同一类型),你可以通过”gmock/gmock-matchers.h”学习这个技巧,但这又要了解太多。

幸运的是,大多数时间你在MakePolymorphicMatcher()的帮助下, 很容易定义一个多形匹配器。下面是以定义NotNull()为例:

using ::testing::MakePolymorphicMatcher;

using ::testing::MatchResultListener;

using ::testing::NotNull;

using ::testing::PolymorphicMatcher;

 

class NotNullMatcher {

 public:

  // To implement a polymorphic matcher, first define a COPYABLE class

  // that has three members MatchAndExplain(), DescribeTo(), and

  // DescribeNegationTo(), like the following.

 

  // In this example, we want to use NotNull() with any pointer, so

  // MatchAndExplain() accepts a pointer of any type as its first argument.

  // In general, you can define MatchAndExplain() as an ordinary method or

  // a method template, or even overload it.

  template <typename T>

  bool MatchAndExplain(T* p,

                       MatchResultListener* /* listener */) const {

    return p != NULL;

  }

 

  // Describes the property of a value matching this matcher.

  void DescribeTo(::std::ostream* os) const { *os << "is not NULL"; }

 

  // Describes the property of a value NOT matching this matcher.

  void DescribeNegationTo(::std::ostream* os) const { *os << "is NULL"; }

};

 

// To construct a polymorphic matcher, pass an instance of the class

// to MakePolymorphicMatcher().  Note the return type.

inline PolymorphicMatcher<NotNullMatcher> NotNull() {

  return MakePolymorphicMatcher(NotNullMatcher());

}

...

 

EXPECT_CALL(foo, Bar(NotNull()));  // The argument must be a non-NULL pointer

注意:你的多形匹配器类不需要继承任何MatcherInterface或是其它类,它的函数也不需要是虚函数。

你可以像在单形匹配器中的一样,将更多的信息输出到MatchAndExplain()中的listener参数中。

Writing New Cardinalities

    Times()中的Cardinality用于告诉Google Mock调用发生多少次。它不必是准确的。比如,你可以说AtLeast(5)Between(2,4)

    如果内置的cardinality集合不适合你,你可以通过下面的接口定义你自己的cardinality

class CardinalityInterface {

 public:

  virtual ~CardinalityInterface();

 

  // Returns true iff call_count calls will satisfy this cardinality.

  virtual bool IsSatisfiedByCallCount(int call_count) const = 0;

 

  // Returns true iff call_count calls will saturate this cardinality.

  virtual bool IsSaturatedByCallCount(int call_count) const = 0;

 

  // Describes self to an ostream.

  virtual void DescribeTo(::std::ostream* os) const = 0;

};

比如,指定一个调用必须发生偶数次,你可以写:

using ::testing::Cardinality;

using ::testing::CardinalityInterface;

using ::testing::MakeCardinality;

 

class EvenNumberCardinality : public CardinalityInterface {

 public:

  virtual bool IsSatisfiedByCallCount(int call_count) const {

    return (call_count % 2) == 0;

  }

 

  virtual bool IsSaturatedByCallCount(int call_count) const {

    return false;

  }

 

  virtual void DescribeTo(::std::ostream* os) const {

    *os << "called even number of times";

  }

};

 

Cardinality EvenNumber() {

  return MakeCardinality(new EvenNumberCardinality);

}

...

 

EXPECT_CALL(foo, Bar(3));

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

历史上的今天

评论

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

页脚

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