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

Koala++'s blog

计算广告学 RTB

 
 
 

日志

 
 

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

2012-04-28 20:19:54|  分类: C++ |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |

Using Matchers in Google Test Assertions

    因为Matchers基本上就是Predicates,所以这就提供了一种在Google Test中使用它们的好方法。它叫ASSERT_THATEXPECT_THAT

  ASSERT_THAT(value, matcher);  // Asserts that value matches matcher.

  EXPECT_THAT(value, matcher);  // The non-fatal version.

    例如,在Google Test中你可以写:

#include "gmock/gmock.h"

 

using ::testing::AllOf;

using ::testing::Ge;

using ::testing::Le;

using ::testing::MatchesRegex;

using ::testing::StartsWith;

...

 

  EXPECT_THAT(Foo(), StartsWith("Hello"));

  EXPECT_THAT(Bar(), MatchesRegex("Line \\d+"));

  ASSERT_THAT(Baz(), AllOf(Ge(5), Le(10)));

    上面的代码( 正如你所猜测的 )执行Foo()Bar(),和Baz(),并验证:

l  Foo()返回一个以”Hello”开头的字符串。

l  Bar()返回一个匹配”Line \\d+”的正则表达式。

l  Baz()返回一个在[5, 10]区间内的数字。

这些宏带来的好处是它们读起来像是英语。它们也会产生提示消息。比如,如果第一个EXPECT_THAT失败,消息会类似下面的:

Value of: Foo()

  Actual: "Hi, world!"

Expected: starts with "Hello"

荣誉:(ASSERT|EXPECT)_THAT的想来是从Hamcrest中获取的,它以assertThat加入JUnit中。

Using Predicates as Matchers

    Google Mock提供了一系列的内置Matchers。如果发现它们还是不够,你可以用一个任意的一元Predicate函数或是仿函数作为一个Matcher,只要它能接受你想用的类型。你就可以将这个Predicate入到Truly()函数中,比如:

using ::testing::Truly;

 

int IsEven(int n) { return (n % 2) == 0 ? 1 : 0; }

...

 

// Bar() must be called with an even number.

EXPECT_CALL(foo, Bar(Truly(IsEven)));

    注意Predicate函数/仿函数不需要一定返回bool类型。它只要求返回值可以用于if (condition)语句中的condition

Matching Arguments that Are Not Copyable

    当你设置一个EXPECT_CALL(mock_obj, Foo(bar))时,Google Mock会保存bar的一个拷贝。当Foo()被调用时之后时,Google Mock会比较传递给Foo的参数和所保存的bar的拷贝。通过这种方式,你不需要担心在EXPECT_CALL()执行之后bar被修改了或是被销毁了。当你使用如Eq(bar)Le(bar)等等Matcher时也是这样。

    但如果bar对象不能拷贝( 比如,没有拷贝构造函数 )?你可以定义自己的Matcher并将它放到Truly()中,前几小节已经介绍过如何去做了。或是如果你自己可以保证bar不会在调用EXPECT_CALL之后改变,这样你可以轻松点。只需要告诉Google Mock它应该保存bar的引用 ,而不是去拷贝它。下面是一个例子:

using ::testing::Eq;

using ::testing::ByRef;

using ::testing::Lt;

...

// Expects that Foo()'s argument == bar.

EXPECT_CALL(mock_obj, Foo(Eq(ByRef(bar))));

 

// Expects that Foo()'s argument < bar.

EXPECT_CALL(mock_obj, Foo(Lt(ByRef(bar))));

切记:如果你这样做,不要在调用EXPECT_CALL之后改变bar对象,否则结果是未定义的。

Validating a Member of an Object

    通常Mock函数将一个对象的引用作为参数。当匹配这个参数时,你可能不想将整个对象与一个固定的对象比较,因为这样过于精确了。相反,你可能想验证几个特定的对象成员或是几个特定的getter函数的结果。你可以用Field()Property来实现这个功能。具体地讲:

Field(&Foo::bar, m)

    这是一个匹配Foo对象的bar成员满足Machter m的一个Matcher

Property(&Foo::baz, m)

    这是一个匹配Foo对象的baz()函数返回的值满足Matcher m的一个Matcher

例如:

Field(&Foo::number, Ge(3))

Property(&Foo::name, StartsWith("John "))

分别表示:

匹配x.number >=3 x对象。

匹配x.name()”John ”开头的x对象。

    注意,在Property(&Foo::baz, ...)中,函数baz()必须是无参的,而且需要声明为const

    随便提一下,Field()Property()同样可以匹配指向对象的普通指针,比如:

Field(&Foo::number, Ge(3))

    它的意思是匹配一个p->number>=3的普通指针p,如果pNULL,匹配总是会失败。

    如果你想一次验证多个成员呢?切记你可以使用AllOf()

Validating the Value of Pointed to by a Pointer Argument

    C++函数经常使用指针型参数。你可以用如NULLNotNULL()以及其它的一些比较Matcher去匹配一个指针,但如果你想通过指针所指向的值去匹配呢?你可以用Pointee(m) Matcher

    Pointee(m)当且仅当指针指向的值匹配m时才匹配一个指针。比如:

using ::testing::Ge;

using ::testing::Pointee;

...

EXPECT_CALL(foo, Bar(Pointee(Ge(3))));

    上面的代码会在传入Bar函数的指针参数所指向的值大于3才匹配。

    Pointee()的一个亮点是它将NULL指针视为匹配失败,所以你可以写Pointee(m)而不用写:

  AllOf(NotNull(), Pointee(m))

    以这种方式来避免NULL使你的测试崩溃。

    是否我已经告诉你Pointee()还可以使用智能指针( linked_ptrshared_ptr scoped_ptr,等等 )

    如果一个指向指针的指针呢?你可以嵌套使用Pointee来匹配值。比如,Pointee(Pointee(Lt(3)))匹配一个指向一个指向一个大于3的指针的指针( 好绕呀 )

Testing a Certain Property of an Object

    有时你想指定一个对象参数有某种属性,但又没有现有的Matcher来指定。如果你想要一个好的错误信息,你需要定义一个Matcher。如果你想简单粗暴地解决,你可以用写一个普通函数来解决这个问题。

    例如你有一个接受Foo类型对象的Mock函数,Foo有一个int bar()函数一个int baz()函数,并且你想限定参数对象的bar()的值加上baz()的值等于某个值。你可以像下面这样做:

using ::testing::MatcherInterface;

using ::testing::MatchResultListener;

 

class BarPlusBazEqMatcher : public MatcherInterface<const Foo&> {

public:

explicit BarPlusBazEqMatcher(int expected_sum)

  : expected_sum_(expected_sum) {}

 

virtual bool MatchAndExplain(const Foo& foo,

                        MatchResultListener* listener) const {

return (foo.bar() + foo.baz()) == expected_sum_;

}

 

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

*os << "bar() + baz() equals " << expected_sum_;

}

 

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

*os << "bar() + baz() does not equal " << expected_sum_;

}

private:

const int expected_sum_;

};

 

inline Matcher<const Foo&> BarPlusBazEq(int expected_sum) {

return MakeMatcher(new BarPlusBazEqMatcher(expected_sum));

}

 

...

 

EXPECT_CALL(..., DoThis(BarPlusBazEq(5)))...;

Matching Containers

    有时候Mock函数中的参数是STL容器( 比如:listvectormap... ),你可能想去匹配参数,因为大多STL容器支持==操作符,所以你可以写Eq(expected_container),或是直接就写expected_container去匹配一个容器。

    可能有时你想更灵活一些( 比如,你想第一个元素精确匹配,第二个元素是一个正数,等等 )。同时,用于测试的容器通常都只有很少一些元素,再说定义一个期望的容器也有些麻烦。

    你可以在下面的情况中用ElementsAre() Matcher

using ::testing::_;

using ::testing::ElementsAre;

using ::testing::Gt;

...

 

MOCK_METHOD1(Foo, void(const vector<int>& numbers));

...

 

EXPECT_CALL(mock, Foo(ElementsAre(1, Gt(0), _, 5)));

    上面Matcher是指container必须有4个元素,分别是1,大于0,任意值,和5

    重载的ElementsAre()可以取010个参数。如果你需要指定更多参数,你可以把它们放到C风格的数组中并且ElementsAreArray()

using ::testing::ElementsAreArray;

...

 

// ElementsAreArray accepts an array of element values.

const int expected_vector1[] = { 1, 5, 2, 4, ... };

EXPECT_CALL(mock, Foo(ElementsAreArray(expected_vector1)));

 

// Or, an array of element matchers.

Matcher<int> expected_vector2 = { 1, Gt(2), _, 3, ... };

EXPECT_CALL(mock, Foo(ElementsAreArray(expected_vector2)));

    如果是数组需要动态创建的情况( 所以数组的大小不可能在编译时知道 ),你可以给ElementsAreArray()一个附加的参数指定数组的大小:

using ::testing::ElementsAreArray;

...

int* const expected_vector3 = new int[count];

... fill expected_vector3 with values ...

EXPECT_CALL(mock, Foo(ElementsAreArray(expected_vector3, count)));

技巧:

ElementsAre*()可以用于任意实现了STL iterator概念的容器( 比如它有一个const_iterator并支持begin()end() )并且支持size(),它不仅支持STL中的容器,也支持任何满意上述两个条件的任何容器。

你可以用嵌套的ElementAre*()去匹配嵌套的( 多维 )容器。

如果容器是通过指针而不是引用传递的,你只需要写Pointee(ElementAre*(...))

顺序对于ElementsAre*()是有影响的。所以不要将它用于顺序是不确定的容器( 比如,hash_map )

Sharing Matchers

    本质上,一个Google Mock Matcher对象包含一个指向引用计数的对象。拷贝Matchers是允许的并且很高效,因为只是指针被拷贝了。当最后一个引用实现对象的Matcher生命结束时,实现对象也会被释放。

    所以,如果你有一些复杂的Matcher,你想重复使用,是不需要每次都创建一个的。只需要将它赋值给另一个Matcher变量,并使用那个变量!比如:

  Matcher<int> in_range = AllOf(Gt(5), Le(10));

... use in_range as a matcher in multiple EXPECT_CALLs ...

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

历史上的今天

评论

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

页脚

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