Google Mock进阶篇 (Google Mock Cookbook译文)
原文链接:http://quweiprotoss.blog.163.com/blog/static/40882883201222721548449/
Google C++ Mocking CookbookVersion: 0.32
作者:Adrian Alexander
译者:Koala++ /屈伟
最新pdf版下载
你来对地方了,这里你可以找到Google Mock的使用方法,但如果你还没有读过启蒙篇,建议你还是先去读一下吧,了解些基本知识。
注意,Google Mock定义在testing命名空间中。你可以用using ::testing::Foo来让代码有更好的可读性。在本文中为了简洁起见,并不采用这种写法,但是在你自己的代码中应该用using。
你必须将Mock函数定义( MOCK_METHOD* )放到Mock类的public:部分中,无论被Mock的函数在基类中是public,protected,还是private。这样做是为了让ON_CALL和EXPECT_CALL可以从Mock类外引用Mock函数。(是的,C++允许子类改变一个基类虚函数的访问权限)。比如:
classFoo {
public:
...
virtualbool Transform(Gadget* g) = 0;
protected:
virtualvoidResume();
private:
virtualintGetTimeOut();
};
classMockFoo :publicFoo {
public:
...
MOCK_METHOD1(Transform, bool(Gadget* g));
// The following must be in the public section, even though the
// methods are protected or private in the base class.
MOCK_METHOD0(Resume, void());
MOCK_METHOD0(GetTimeOut, int());
};
Mock重载函数的方法也是一样的,不需要使用别的方式:
class Foo {
...
// Must be virtual as we'll inherit from Foo.
virtual ~Foo();
// Overloaded on the types and/or numbers of arguments.
virtualintAdd(Element x);
virtualintAdd(int times, Element x);
// Overloaded on the const-ness of this object.
virtual Bar&GetBar();
virtualconst Bar&GetBar() const;
};
class MockFoo :publicFoo {
...
MOCK_METHOD1(Add, int(Element x));
MOCK_METHOD2(Add, int(int times, Element x);
MOCK_METHOD0(GetBar, Bar&());
MOCK_CONST_METHOD0(GetBar, const Bar&());
};
注意:如果你并不Mock所有的重载函数,编译器会警告你基类中的一些函数被隐藏了。修正的方法是用using将它们引入域中:
class MockFoo :publicFoo {
...
using Foo::Add;
MOCK_METHOD1(Add, int(Element x));
// We don't want to mock int Add(int times, Element x);
...
};
Mock一个模板类,需要在MOCK_*宏后加上_T:
template <typenameElem>
classStackInterface {
...
// Must be virtual as we'll inherit from StackInterface.
virtual ~StackInterface();
virtualintGetSize() const = 0;
virtualvoidPush(constElem& x) = 0;
};
template <typenameElem>
classMockStack :publicStackInterface<Elem> {
...
MOCK_CONST_METHOD0_T(GetSize, int());
MOCK_METHOD1_T(Push, void(const Elem& x));
};
Google Mock可以Mock非虚函数用在hi-perf dependency injection中。
Mock非虚函数时并不与真实的类共享一个公共的基类,你的Mock类与真实类将毫无关系,但两者所定义的函数却是一致的。Mock非虚函数与Mock虚函数的语法是一致的:
// A simple packet stream class. None of its members is virtual.
classConcretePacketStream {
public:
voidAppendPacket(Packet* new_packet);
const Packet*GetPacket(size_t packet_number) const;
size_t NumberOfPackets() const;
...
};
// A mock packet stream class. It inherits from no other, but defines
// GetPacket() and NumberOfPackets().
classMockPacketStream {
public:
MOCK_CONST_METHOD1(GetPacket, const Packet*(size_t packet_number));
MOCK_CONST_METHOD0(NumberOfPackets, size_t());
...
};
注意与真实类不同的是Mock类没有定义AppenPacket(),但只要测试中没有调用到这个函数,这种写法是没有问题的。
接下来,你需要想出一种在正式代码中使用ConcretePacketStream,在测试代码中使用MockPacketStream的方法。因为函数是非虚的,而两个类也是毫无关系的,所以你必须在编译时(而不是运行时 )决定你使用的类。
其中一种方法是模板化需要用Packet Stream的代码。具体一点,你在代码中使用一个针对packet stream模板参数。在正式代码中,你可以用ConcretePacketStream来实例化,在测试中你用MockPacketStream来实例化。下面是一个例子:
template <classPacketStream>
voidCreateConnection(PacketStream* stream) {... }
template <classPacketStream>
classPacketReader {
public:
voidReadPackets(PacketStream* stream, size_t packet_num);
};
然后你可以在正式代码中使用CreateConnection<ConcretePacketStream>()和PacketReader<ConcretePacketStream>,在测试代码中使用CreateConnection<MockPacketStream>和PacketReader<MockPacketStream>:
MockPacketStream mock_stream;
EXPECT_CALL(mock_stream, ...)...;
.. set more expectations on mock_stream ...
PacketReader<MockPacketStream> reader(&mock_stream);
... exercise reader ...
可以使用Google Mock来Mock一个自由函数(比如,普通C风格函数或是静态函数 )。但你需要一个接口(抽象类 )重写你的代码。
Mock并不直接调用自由函数(暂且称之为OpenFile ),而是为它引入一个接口,并需要针对这个接口实现对函数对自由函数的调用:
class FileInterface {
public:
...
virtualbool Open(constchar* path,constchar* mode) = 0;
};
classFile :public FileInterface {
public:
...
virtualbool Open(constchar* path,constchar* mode) {
return OpenFile(path, mode);
}
};
你的代码可以通过FileInterface打开一个文件,现在函数更容易被Mock。
这看起来太麻烦了,但在现实中你通常可以将多个相关的函数放到一个接口中,所以为每个函数定义一个接口这种额外工作会少很多。
如果一个没有指定EXPECT_CALL的Mock函数被调用了,Google Mock会打印一个”uninteresting call”警告。这样做的合理性如下:
l 当测试写完之后,可能有新的函数加入到接口中。而我们不能仅因为一个测试它不知道某个函数要被调用就失败。
l 但是,这种情况也可能意味着测试中有bug,所以Google Mock也不能什么都不提示。如果用户认为这些调用是无关的,它可以加入一个EXPECT_CALL来消除警告。
但是,有时你可能想消除所有的”uninteresting call”警告,但有时你可能想做刚好相反的事,即认为所有的”uninteresting call”都是错误。Google Mock能让你在Mock对象这个级别上选择你的决定。
TEST(...) {
MockFoo mock_foo;
EXPECT_CALL(mock_foo, DoThis());
... code that uses mock_foo ...
}
如果mock_foo中一个不是DoThis的函数被调用了,Google Mock会给出一个警告,但是你用NiceMock<MockFoo>重写你的测试,警告会消失,你会得到一个更清爽的输出:
using ::testing::NiceMock;
TEST(...) {
NiceMock<MockFoo> mock_foo;
EXPECT_CALL(mock_foo, DoThis());
... code that uses mock_foo ...
}
NiceMock是MockFoo的一个子类,所以它在任何接受MockFoo类型的地方使用。
在MockFoo的构造函数是有参数的时候也是可以用的,因为NiceMock<MockFoo>“继承”了MockFoo的构造函数。
using ::testing::NiceMock;
TEST(...) {
NiceMock<MockFoo> mock_foo(5, "hi"); // Calls MockFoo(5, "hi").
EXPECT_CALL(mock_foo, DoThis());
... code that uses mock_foo ...
}
StickMock的用法也是相似的,只是它的目的是让所有“uninteresting call”失败:
using ::testing::StrictMock;
TEST(...) {
StrictMock<MockFoo> mock_foo;
EXPECT_CALL(mock_foo, DoThis());
... code that uses mock_foo ...
// The test will fail if a method of mock_foo other than DoThis()
// is called.
}
下面还有一些说明(我不太喜欢这些说明,但遗憾的是它们是C++限制的副作用 ):
1. NiceMock<MockFoo>和StickMock<MockFoo>仅在直接在MockFoo类中使用MOCK_METHOD*定义的Mock函数。如果一个Mock函数在MockFoo的基类中定义,那么“nice”或是“strict”是否会影响它则取决于编译器。特别要指出的是,嵌套的NiceMock和StrickMock是不支持的(比如,NiceMock<StrictMock<MockFoo> >)。
2. Mock基类( MockFoo )不能传递非常量引用给构造函数,因为这种做法被Google C++ 编码规范禁止了。
3. 在构造函数和析构函数运行中,Mock对象不是nice也不是strict。如果在这个对象的构造函数或是析构函数中调用一个Mock函数,可能会因为这个原因造成意外。(译注:Effective C++ Item 8)。
最后,你必须在使用这个特性时特别小心,因为你所做的这个决定会应用到Mock类未来所有的改动上。如果你所Mock的接口做了一个重要的改变,它会让你的测试(如果你用StrictMock )失败或是在没有警告提示的情况下让bug溜过(如果你使用NiceMock )。所以,应该显式地调用EXPECT_CALL来指定mock的行为,仅在最后将Mock对象换为NiceMock或是StrictMock的。
有时候一个函数有相当长的参数列表,那Mock的时候是相当无趣的,比如:
classLogSink {
public:
...
virtualvoid send(LogSeverity severity,constchar* full_filename,
constchar* base_filename,int line,
conststruct tm* tm_time,
constchar* message, size_t message_len) = 0;
};
这个函数的参数列表很长且难用(这么说吧,message参数甚至都不是以’\0’结尾的 )。如果我们执意要Mock它,那结果必是不雅的。然而如果我们试着简化这个接口,又需要将所有使用这个接口的代码全部改了,这通常是不可行的。
技巧就是在Mock类中修改这个函数:
classScopedMockLog :publicLogSink {
public:
...
virtualvoid send(LogSeverity severity,constchar* full_filename,
constchar* base_filename,int line,const tm* tm_time,
constchar* message, size_t message_len) {
// We are only interested in the log severity, full file name, and
// log message.
Log(severity, full_filename, std::string(message, message_len));
}
// Implements the mock method:
//
// void Log(LogSeverity severity,
// const string& file_path,
// const string& message);
MOCK_METHOD3(Log, void(LogSeverity severity,const string& file_path,
const string& message));
};
通过定义一个新有较少参数的Mock函数,我们让Mock类更易用。
你经常会发现你正在用一些没有针对接口实现的类。你为了可以用这种类(且称为Concrete类 )来测试自己的代码,你可能会试着将Concrete的函数变为虚函数,然后再去Mock它。
请不要这样做。
将非虚函数改为虚函数是一个重大决定。这样做之后,子类会改变父类的行为。这样就会更难保持类的不变性,而从降低了你对类的控制力。你只应在一个合理的理由下将非虚函数变为虚函数。
直接Mock具体的类会产生类和测试的高度耦合,任何对类的小的改动都会让你测试失效,这会让你陷入维护测试的痛苦中。
为了避免这种痛苦,许多程序员开始了“针对接口”的实践:并不直接调用Concrete类,而是定义一个接口去调用Concrete类。然后你在Concrete类之上实现这个接口,即配接器。
这种技术可能会带来一些负担:
l 你要为虚函数的调用买单(通常不是问题 )
l 程序员需要掌握更多的抽象
但是,它同时也能巨大的好处,当然也有更好的可测性:
l Concrete的API也许并不是很适合你的问题领域,因为你可能不是这个API唯一的调用方。通过设计你自己的接口,你有一个将这个类修改成自己所需的类的机会,你可加入一些特定功能,重命名接口函数,等等,你可以做的不是只是减少几个自己不使用的API。这可以让你自己以更自然的方式实现你的代码,因为它有更好的可读性,更好的可维护性,你也会有更高的编程效率。
l 如果Concrete的实现改变了,你不需要重写与改动相关的所有测试。相反你可以将改动在你自己的接口中隐藏,使你的调用代码和测试与Concrete改动绝缘。
有些人会担心如果每个人都在实践这个技术,将会产生大量的重复代码。这个担心是可以理解的。但是,有两个理由可以证明这种情况可能不会发生。
l 不同的工程可能会以不同的方式使用Concrete,所以最适合每个工程的接口是不同的。所以每个工程都有在Concrete之上的自己的领域相关的接口,这些接口是各不相同的。
l 如果有很多的工程用相同的接口,它们可以共用一个接口,就像它们共用Concrete一样。你可以在Concrete类的旁边提交接口和配接器的代码(也许是在一个contrib子目录中 )并让许多工程使用它。
你需要仔细衡量针对你特定问题这种做法的优缺点,但我可以向你保证的是:Java世界的人已经实践这种方法很久了,并且它已经被证明在很广泛的领域中是一种有效的技术。
有时你已经有一个对某一接口的Fake实现了。比如:
classFoo {
public:
virtual~Foo() {}
virtualcharDoThis(int n) = 0;
virtualvoidDoThat(constchar* s,int* p) = 0;
};
classFakeFoo :publicFoo {
public:
virtualcharDoThis(int n) {
return (n > 0) ?'+' :
(n < 0) ? '-' :'0';
}
virtualvoidDoThat(constchar* s,int* p) {
*p = strlen(s);
}
};
现在你想要Mock这个接口,比如你想在它上面设置期望。但是你还想用FakeFoo作为Mock类函数的默认行为,当然你可以选择将代码复制到Mock对象里,但是这会有很大的工作量。
当你用Google Mock来定义Mock类,你可以代理对象的默认行为给你已经有的Fake类,用下面的方法:
using ::testing::_;
using ::testing::Invoke;
classMockFoo :publicFoo {
public:
// Normal mock method definitions using Google Mock.
MOCK_METHOD1(DoThis, char(int n));
MOCK_METHOD2(DoThat, void(constchar* s,int* p));
// Delegates the default actions of the methods to a FakeFoo object.
// This must be called *before* the custom ON_CALL() statements.
voidDelegateToFake() {
ON_CALL(*this, DoThis(_))
.WillByDefault(Invoke(&fake_, &FakeFoo::DoThis));
ON_CALL(*this, DoThat(_, _))
.WillByDefault(Invoke(&fake_, &FakeFoo::DoThat));
}
private:
FakeFoofake_; // Keeps an instance of the fake in the mock.
};
你现在可以像以前一样在你的测试中使用MockFoo。只是你要记得如果你没有明确地设置ON_CALL或是EXPECT_CALL()的行为,那Fake函数就会被调用:
using ::testing::_;
TEST(AbcTest, Xyz) {
MockFoo foo;
foo.DelegateToFake(); // Enables the fake for delegation.
// Put your ON_CALL(foo, ...)s here, if any.
// No action specified, meaning to use the default action.
EXPECT_CALL(foo, DoThis(5));
EXPECT_CALL(foo, DoThat(_, _));
int n = 0;
EXPECT_EQ('+', foo.DoThis(5)); // FakeFoo::DoThis() is invoked.
foo.DoThat("Hi", &n); // FakeFoo::DoThat() is invoked.
EXPECT_EQ(2, n);
}
一些技巧:
l 如果你不想用FakeFoo中的函数,你仍然可以通过在ON_CALL或是在EXPECT_CALL中用.WillOnce() / .WillRepeated()覆盖默认行为。
l 在DelegateToFake()中,你只需要代理那些你要用的函数的Fake实现。
l 这里所讲的技术对重载函数也是适用的,但你需要告诉编译器你是指重载函数中的哪一个。消除一个Mock函数的歧义(即你在ON_CALL中指定的 ),参见“Selecting Between Overloaded Functions”一节,消除一个Fake函数的歧义(即在Invoke中的 ),使用static_cast来指定函数的类型。
l 将一个Mock和一个Fake搅在一起通常是某种错误的信号。也许你还没有习惯基于交互方式的测试。或是你的接口融合了过多的角色,应该把这个接口分开。所以别滥用这个技术。我们建议这仅应该用做你重构你代码时的中间步骤。
再思考一个Mock和一个Fake混在一起的问题,这里有一个例子来说明为什么这是一个错误信号:假设你有一个System类,它实现了一些低层的系统操作。具体一些,它处理文件操作和I/O操作。假设你想测试你的代码是如何使用System来进行I/O操作,你只是想让文件操作工作正常就可以了。如果你想要Mock整个System类,你就必须提供一个关于文件操作的Fake实现,这表明System拥有了太多的角色。
相反,你可以定义一个FileOps和一个IOOps接口来拆分System的功能。然后你可以Mock IOOps而不用Mock FileOps。
当使用测试doubles(替身的意思 mocks, fakes, stubs等等 )时,有时它们的行为与真实对象的行为不一样。这种差别可能是有意为之(比如模拟一个错误,假设你的代码中有错误处理逻辑 )或是无意的。如果你的Mock与真实对象的差别是错误造成的,你可能会得到能通过测试,却在正式代码中失败的代码。
你可以使用delegating-to-real技术来保证你的Mock与真实对象有着相同的行为,并且拥有验证调用的能力。这个技术与delegating-to-fake技术很相似,区别在于我们使用真实对象而不是一个Fake。下面是一个例子:
using ::testing::_;
using ::testing::AtLeast;
using ::testing::Invoke;
class MockFoo :publicFoo {
public:
MockFoo() {
// By default, all calls are delegated to the real object.
ON_CALL(*this, DoThis())
.WillByDefault(Invoke(&real_, &Foo::DoThis));
ON_CALL(*this, DoThat(_))
.WillByDefault(Invoke(&real_, &Foo::DoThat));
...
}
MOCK_METHOD0(DoThis, ...);
MOCK_METHOD1(DoThat, ...);
...
private:
Foo real_;
};
...
MockFoo mock;
EXPECT_CALL(mock, DoThis())
.Times(3);
EXPECT_CALL(mock, DoThat("Hi"))
.Times(AtLeast(1));
... use mock in test ...
用上面的代码,Google Mock会验证你的代码是否做了正确的调用(有着正确的参数,以正确的顺序,有着正确的调用次数 ),并且真实的对象会处理这些调用(所以行为将会和正式代码中表现一致 )。这会让你在两个世界都表现出色。
理想中,你应该针接口编程,并接口的函数都是虚函数。现实中,有时候你需要Mock一个非纯虚函数(比如,它已经有了实现 )。比如:
class Foo {
public:
virtual ~Foo();
virtualvoid Pure(int n) = 0;
virtualint Concrete(constchar* str) { ... }
};
class MockFoo :publicFoo {
public:
// Mocking a pure method.
MOCK_METHOD1(Pure, void(int n));
// Mocking a concrete method. Foo::Concrete() is shadowed.
MOCK_METHOD1(Concrete, int(constchar* str));
};
有时你想调用Foo::Concrete()而不是MockFoo::Concrete()。也许你想将它做为Stub行为的一部分,或是也许你的测试根本不需要Mock Concrete() (当你不需要Mock一个新的Mock类的任何一个函数时候,而定义一个新的Mock类时,将会是出奇的痛苦)。
解决这个问题的技巧就是在你的Mock类中留下一个后门,可以通过它去访问基类中的真实函数:
class MockFoo :publicFoo {
public:
// Mocking a pure method.
MOCK_METHOD1(Pure, void(int n));
// Mocking a concrete method. Foo::Concrete() is shadowed.
MOCK_METHOD1(Concrete, int(constchar* str));
// Use this to call Concrete() defined in Foo.
intFooConcrete(constchar* str) {returnFoo::Concrete(str); }
};
现在你可以在一个动作中调用Foo::Concrete():
using ::testing::_;
using ::testing::Invoke;
...
EXPECT_CALL(foo, Concrete(_))
.WillOnce(Invoke(&foo, &MockFoo::FooConcrete));
或是告诉Mock对象你不想MockConcrete():
using ::testing::Invoke;
...
ON_CALL(foo, Concrete(_))
.WillByDefault(Invoke(&foo, &MockFoo::FooConcrete));
( 为什么我们不只写Invoke(&foo, &Foo::Concrete)?如果你这样做,MockFoo::Concrete会被调用(从而导致无穷递归),因为Foo::Concrete()是虚函数。这就是C++的工作方式 )。
你可以精确指定一个Mock函数期望的参数是什么:
using ::testing::Return;
...
EXPECT_CALL(foo, DoThis(5))
.WillOnce(Return('a'));
EXPECT_CALL(foo, DoThat("Hello", bar));
你可以用Matchers去匹配有一定特征的参数:
using ::testing::Ge;
using ::testing::NotNull;
using ::testing::Return;
...
EXPECT_CALL(foo, DoThis(Ge(5))) // The argument must be >= 5.
.WillOnce(Return('a'));
EXPECT_CALL(foo, DoThat("Hello", NotNull()));
// The second argument must not be NULL.
一个常用的Matcher是_,它表示匹配任何参数:
using ::testing::_;
using ::testing::NotNull;
...
EXPECT_CALL(foo, DoThat(_, NotNull()));
你可以使用已有的AllOf(),AnyOf(),Not(),组合产生一些复杂的Matchers:
using ::testing::AllOf;
using ::testing::Gt;
using ::testing::HasSubstr;
using ::testing::Ne;
using ::testing::Not;
...
// The argument must be > 5 and != 10.
EXPECT_CALL(foo, DoThis(AllOf(Gt(5),
Ne(10))));
// The first argument must not contain sub-string "blah".
EXPECT_CALL(foo, DoThat(Not(HasSubstr("blah")),
NULL));
Google Matchers都是静态类型的,即如果你错误地使用Matcher的类型(比如,如果你使用Eq(5)去匹配一个string参数 ),编译器会报错。这也是为你好。
但有时,你知道你自己在做什么,并希望编译器放你一马。举例来说:如果你有一个针对long类型的Matcher,但你想匹配的是int。虽然这两个类型不同,但实际上用Matcher<long>去匹配int并没有错,毕竟,我们可以先将int参数转换成long,再传给Matcher。
为了支持这种需求,Google Mock提供了SafeMatcherCast<T>(m)函数。它将一个Matcher m转换成Matcher<T>。为了保证它是安全的转换,Google Mock如检查(令U为m接受的参数 ):
1. T类型可以隐式地转换为U类型。
2. 当T和U都是内置数值类型时( bool,integers,float ),从T到U的转换是无损的(换句话说,用T类型的任何值都可以用U类型表示 )。
3. 当U是一个引用,T必须也是一个引用(因为底层的Matcher也许会对U类型参数的地址感兴趣 )。
如果上述条件中任何一个没满足,是不会通过编译的。
下面是一个例子:
using ::testing::SafeMatcherCast;
// A base class and a child class.
classBase { ... };
classDerived :publicBase { ... };
classMockFoo :public Foo {
public:
MOCK_METHOD1(DoThis, void(Derived* derived));
};
...
MockFoo foo;
// m is a Matcher<Base*> we got from somewhere.
EXPECT_CALL(foo, DoThis(SafeMatcherCast<Derived*>(m)));
如果你发现SafeMatcherCast<T>(m)太严格,你可以用一个类似的函数MatcherCast<T>(m)。两个函数的区别是如果static_cast可以将T类型转换成类型U,那么MatcherCast就可以转换。
MatcherCast对你凌驾于C++类型系统之上很重要的( static_cast不总是安全的,比如它可以丢弃一部分信息 ),所以要小心不要误用/滥用它。
如果你期望一个重载函数被调用,编译器需要你来指明你指的是重载函数中的哪一个。
消除一个对象上关于常量的重载的歧义,使用Const()来指明。
using ::testing::ReturnRef;
class MockFoo :public Foo {
...
MOCK_METHOD0(GetBar, Bar&());
MOCK_CONST_METHOD0(GetBar,const Bar&());
};
...
MockFoo foo;
Bar bar1, bar2;
EXPECT_CALL(foo, GetBar()) // The non-const GetBar().
.WillOnce(ReturnRef(bar1));
EXPECT_CALL(Const(foo), GetBar()) // The const GetBar().
.WillOnce(ReturnRef(bar2));
( Const()由Google Mock定义,并返回它的参数的const引用。)
消除函数个数相同,但参数类型不同重载函数的歧义,你也许需要精确指定一个Matcher的匹配类型,在Matcher<type>中修饰你的Matcher,或是使用一个类型是确定的Matcher( TypedEq<type>,An<type>()等等 ):
using ::testing::An;
using ::testing::Lt;
using ::testing::Matcher;
using ::testing::TypedEq;
classMockPrinter :public Printer {
public:
MOCK_METHOD1(Print, void(int n));
MOCK_METHOD1(Print, void(char c));
};
TEST(PrinterTest, Print) {
MockPrinter printer;
EXPECT_CALL(printer, Print(An<int>())); // void Print(int);
EXPECT_CALL(printer, Print(Matcher<int>(Lt(5)))); // void Print(int);
EXPECT_CALL(printer, Print(TypedEq<char>('a'))); // void Print(char);
printer.Print(3);
printer.Print(6);
printer.Print('a');
}
当一个Mock函数被调用时,最后一个有效的期望会被匹配( “新规则覆盖老规则” )。所以你可以让一个函数根据它的参数值去做不同的事,如下:
using ::testing::_;
using ::testing::Lt;
using ::testing::Return;
...
// The default case.
EXPECT_CALL(foo, DoThis(_))
.WillRepeatedly(Return('b'));
// The more specific case.
EXPECT_CALL(foo, DoThis(Lt(5)))
.WillRepeatedly(Return('a'));
现在,如果foo.DoThis()被调用时参数值小于5,就会返回’a’,否则返回’b’。
有时候只能单独去匹配参数是不够的。比如,我们想设置第一个参数的值必须小于第二个参数的值。With子句可以让我们将Mock函数的参数做为一个整体去匹配。比如:
using ::testing::_;
using ::testing::Lt;
using ::testing::Ne;
...
EXPECT_CALL(foo, InRange(Ne(0), _))
.With(Lt());
上面代码意为InRange第一个参数必须非0,并且必须小于第二个参数。
在With内的语句必须是一个Match<tr1::tuple<A1, ..., An> >类型的Matcher,其中A1,..., An是函数参数的类型。
你还可以用AllArgs(m)来代替将m写在.With()里的写法。两种形式意义相同,但是.With(AllArgs(Lt()))比.With(Lt())更具有可读性。
你可以用Args<k1, ..., kn>(m)根据m规则来匹配n个选择的参数。比如:
using ::testing::_;
using ::testing::AllOf;
using ::testing::Args;
using ::testing::Lt;
...
EXPECT_CALL(foo, Blah(_, _, _))
.With(AllOf(Args<0, 1>(Lt()), Args<1, 2>(Lt())));
为了方便和举例起见,Google Mock提供了关于2-tuples的Matchers,包括上面的Lt() Matcher。可以到CheatSheet中找到完整的列表。
注意如果你想将这些参数传递给你自己的Predicate(比如.With(0, 1)(Truly(&MyPredicate))),你的必须以tr1::tuple做为它的参数。Google Mock会将n个选中的参数作为单个tuple传递给Predicate。
你是否注意到Matcher只是一个好看一些的Pridicate,许多已有的算法可将Predicates作为参数(比如,那些在STL的<algorithm>中定义的算法),如果Google Mock Matchers不能参与到其中,那将是一个遗憾。
幸运的地,你可以将一元Predicate仿函数放到Matches()函数中来使用Matcher,比如:
#include<algorithm>
#include<vector>
std::vector<int> v;
...
// How many elements in v are >= 10?
constint count = count_if(v.begin(), v.end(), Matches(Ge(10)));
因为你可通过将简单的matchers组合来产生复杂的matchers,那么这就给你了一种构造组合Predicates的方便方法(与在STL中使用<functional>中的函数一样 )。比如,下面是一个任何满足 >=0,<=100,!=50的Predicate。
Matches(AllOf(Ge(0), Le(100), Ne(50)))
因为Matchers基本上就是Predicates,所以这就提供了一种在Google Test中使用它们的好方法。它叫ASSERT_THAT和EXPECT_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中。
Google Mock提供了一系列的内置Matchers。如果发现它们还是不够,你可以用一个任意的一元Predicate函数或是仿函数作为一个Matcher,只要它能接受你想用的类型。你就可以将这个Predicate入到Truly()函数中,比如:
using ::testing::Truly;
intIsEven(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。
当你设置一个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对象,否则结果是未定义的。
通常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,如果p是NULL,匹配总是会失败。
如果你想一次验证多个成员呢?切记你可以使用AllOf()。
C++函数经常使用指针型参数。你可以用如NULL,NotNULL()以及其它的一些比较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_ptr,shared_ptr, scoped_ptr,等等 )?
如果一个指向指针的指针呢?你可以嵌套使用Pointee来匹配值。比如,Pointee(Pointee(Lt(3)))匹配一个指向一个指向一个大于3的指针的指针(好绕呀 )。
有时你想指定一个对象参数有某种属性,但又没有现有的Matcher来指定。如果你想要一个好的错误信息,你需要定义一个Matcher。如果你想简单粗暴地解决,你可以用写一个普通函数来解决这个问题。
例如你有一个接受Foo类型对象的Mock函数,Foo有一个int bar()函数一个int baz()函数,并且你想限定参数对象的bar()的值加上baz()的值等于某个值。你可以像下面这样做:
using ::testing::MatcherInterface;
using ::testing::MatchResultListener;
classBarPlusBazEqMatcher :public MatcherInterface<const Foo&> {
public:
explicitBarPlusBazEqMatcher(int expected_sum)
: expected_sum_(expected_sum) {}
virtualboolMatchAndExplain(const Foo& foo,
MatchResultListener* listener) const {
return (foo.bar() + foo.baz()) ==expected_sum_;
}
virtualvoidDescribeTo(::std::ostream* os) const {
*os <<"bar() + baz() equals " <<expected_sum_;
}
virtualvoidDescribeNegationTo(::std::ostream* os) const {
*os <<"bar() + baz() does not equal " <<expected_sum_;
}
private:
constintexpected_sum_;
};
inline Matcher<const Foo&> BarPlusBazEq(int expected_sum) {
return MakeMatcher(new BarPlusBazEqMatcher(expected_sum));
}
...
EXPECT_CALL(..., DoThis(BarPlusBazEq(5)))...;
有时候Mock函数中的参数是STL容器(比如:list,vector,map,... ),你可能想去匹配参数,因为大多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()可以取0到10个参数。如果你需要指定更多参数,你可以把它们放到C风格的数组中并且ElementsAreArray():
using ::testing::ElementsAreArray;
...
// ElementsAreArray accepts an array of element values.
constint 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 =newint[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 )。
本质上,一个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 ...
如果你对一个Mock函数如何被调用不感兴趣,你可以不对它指定任何事。如果你这样做了,当这个函数被调用时,Google Mock会使用它的默认行为使测试可以得以进行下去。如果你不太喜欢这个默认的行为,你可以用DefaultValue<T>::Set()(在这个文档的后面会讲到 )或是ON_CALL去覆盖默认行为。
请注意一旦你对某个Mock函数表现出了兴趣(通过EXPECT_CALL() ),所在对它的调用都需要匹配某个期望。如果这个函数被调用了,但参数没有匹配任何一个EXPECT_CALL语句,它将会产生一个错误。
如果一个Mock函数根本不应该被调用,可以明确地指出:
using ::testing::_;
...
EXPECT_CALL(foo, Bar(_))
.Times(0);
如果对一个函数的某些调用是允许的,其它的调用则不行,则可以列出所有期望的调用:
using ::testing::AnyNumber;
using ::testing::Gt;
...
EXPECT_CALL(foo, Bar(5));
EXPECT_CALL(foo, Bar(Gt(10)))
.Times(AnyNumber());
如果一个调用不匹配任一EXPECT_CALL(),刚它会产生一个错误。
尽管在Google Mock尝试匹配一个设置了期望的函数时,会优先匹配先定义的EXPECT_CALL语句,但默认的调用并不是必须以EXPECT_CALL()所写的顺序进行匹配。比如,如果参数匹配了第三个EXPECT_CALL,但没有匹配前两个EXPECT_CALL,那么就会使用第三个期望。
如果你更好所有调用都以期望的顺序进行,将EXPECT_CALL()语句放到一个InSequence对象的生命周期中:
using ::testing::_;
using ::testing::InSequence;
{
InSequence s;
EXPECT_CALL(foo, DoThis(5));
EXPECT_CALL(bar, DoThat(_))
.Times(2);
EXPECT_CALL(foo, DoThis(6));
}
在这个例子中,我们期望调用以顺序如下:先调用foo.DoThis(5),然后两次参数为任意的bar.DoThat()调用,最后调用一次foo.DoThis()。如果调用的顺序与上面不符,则Google Mock会报告一个错误。
有时要求所有的调用都以一个预定义的顺序会导致测试脆弱。比如,也许我们会关心A在B和C之前调用,但不关心B和C的调用顺序先后。在这种情况下,测试应当反应我们真正的意图,而不是写一个约束过强的语句。
Google Mock在调用上设置一个顺序的任意DAG( directed acyclic graph有向无环图 )。表达有DAG的一种方式是用EXPECT_CALL的After子句。
另一种方法是通过InSequence()子句(不是InSequence类 ),这是我们从jMock 2中借鉴而来的。它比之After()稍失灵活,但当你有一长串顺序调用之时会更方便,因为它不要求你对长串中的期望都起一个不同的名字,下面是它如何工作的:
如果我们视EXPECT_CALL()语句为图中的结点,添加一条从结点A到结点B的边,意思是A必须先于B出现,我们可以得到一个DAG。如果DAG被分解成了单独的顺序,我们只需要知道每个EXPECT_CALL在顺序中的位置,我们就可以重构出原来的DAG。
因此要为指定在期望之上的部分有序我们需要做两件事:第一定义一些Sequence对象,然后指明Sequence在DAG中的部分。在同一顺序(sequence)中的期望必须以定义的先后(order)出现。比如:
using ::testing::Sequence;
Sequence s1, s2;
EXPECT_CALL(foo, A())
.InSequence(s1, s2);
EXPECT_CALL(bar, B())
.InSequence(s1);
EXPECT_CALL(bar, C())
.InSequence(s2);
EXPECT_CALL(foo, D())
.InSequence(s2);
以上代码所指定的顺序为(其中s1是A->B,s2是A->C->D ):
+---> B
|
A ---|
|
+---> C ---> D
这意味着A必须先于B和C出现,并且C必须在D之前出现,并除此之外没有其它的顺序要求。
当一个Mock函数被调用时,Google Mock只考虑那些仍然有效的期望。一个期望在创建之时是有效的,当在它之上发生一次调用后,它就变为失效的了。比如,在:
using ::testing::_;
using ::testing::Sequence;
Sequence s1, s2;
EXPECT_CALL(log, Log(WARNING, _,"File too large.")) // #1
.Times(AnyNumber())
.InSequence(s1, s2);
EXPECT_CALL(log, Log(WARNING, _,"Data set is empty.")) // #2
.InSequence(s1);
EXPECT_CALL(log, Log(WARNING, _,"User not found.")) // #3
.InSequence(s2);
只要#2或#3任一匹配,#1就会失效。如果一个警告”File too large”在此之后调用,它将会产生一个错误。
注意一个期望在它饱和后不会自动失效。例如:
using ::testing::_;
...
EXPECT_CALL(log, Log(WARNING, _, _)); // #1
EXPECT_CALL(log, Log(WARNING, _,"File too large.")); // #2
上面的代码意思是有仅只能有一个”File too large”的警告。如果第二个警告仍然是”File too large”,#2仍然会匹配并且产生一个超出上界的错误。
如果这不是你想要的,你可以让一个期望在饱和之后就失效:
using ::testing::_;
...
EXPECT_CALL(log, Log(WARNING, _, _)); // #1
EXPECT_CALL(log, Log(WARNING, _,"File too large.")) // #2
.RetiresOnSaturation();
如果一个Mock函数的返回类型是引用,你需要用ReturnRef()而不是Return()来返using ::testing::ReturnRef;
classMockFoo :public Foo {
public:
MOCK_METHOD0(GetBar, Bar&());
};
...
MockFoo foo;
Bar bar;
EXPECT_CALL(foo, GetBar())
.WillOnce(ReturnRef(bar));
Return(x)这个动作在创建时就会保存一个x的拷贝,在它执行时总是返回相同的值。但有时你可能不想每次返回x的拷贝。
如果Mock函数的返回类型是引用,你可以用ReturnRef(x)来每次返回不同的值。但是Google Mock不允许在Mock函数返回值不是引用的情况下用ReturnRef()返回,这样做的后果通常是提示一个错误,所以,你应该怎么做呢?
你可以尝试ByRef():
using testing::ByRef;
using testing::Return;
class MockFoo :public Foo {
public:
MOCK_METHOD0(GetValue,int());
};
...
int x = 0;
MockFoo foo;
EXPECT_CALL(foo, GetValue())
.WillRepeatedly(Return(ByRef(x)));
x = 42;
EXPECT_EQ(42, foo.GetValue());
不幸的是,上面的代码不能正常工作,它会提示以下错误:
Value of: foo.GetValue()
Actual: 0
Expected: 42
不能正常工作的原因是在Return(value)这个动作创建时将x转换成Mock函数的返回类型,而不是它执行时再进行转换(这个特性是为保证当值是代理对象引用一些临时对象时的安全性 )。结果是当期望设置时ByRef(x)被转换成一个int值(而不是一个const int& ),且Return(ByRef(x)会返回0。
ReturnPointee(pointer)是用来解决定这个问题的。它在动作执行时返回指针指向的值:
using testing::ReturnPointee;
...
int x = 0;
MockFoo foo;
EXPECT_CALL(foo, GetValue())
.WillRepeatedly(ReturnPointee(&x)); // Note the & here.
x = 42;
EXPECT_EQ(42, foo.GetValue()); // This will succeed now.
你想当一个函数被调用时做更多的事吗?这个需求是合理的。DoAll()允许你每次执行一系列动作。只有最后一个动作的返回值会被使用。
using ::testing::DoAll;
classMockFoo :public Foo {
public:
MOCK_METHOD1(Bar, bool(int n));
};
...
EXPECT_CALL(foo, Bar(_))
.WillOnce(DoAll(action_1,
action_2,
...
action_n));
有时一个函数的作用不是通过返回值来体现,而是通过副作用。比如,你可以改变一些全局状态或是修改一个输入参数的值。要Mock副作用,通常你可以通过实现::testing::AtionInterface定义你自己的动作。
如果你要做的仅仅是改变一个输入参数,内置的SetArgPointee()动作是很方便的:
using ::testing::SetArgPointee;
classMockMutator :public Mutator {
public:
MOCK_METHOD2(Mutate, void(bool mutate, int* value));
...
};
...
MockMutator mutator;
EXPECT_CALL(mutator, Mutate(true, _))
.WillOnce(SetArgPointee<1>(5));
在这个例子中,当mutator.Mutate()被调用时,我们将赋给由第二个参数指针指向的值为5。
SetArgPointee()将传入的值进行了一次拷贝,所以你不需要保证这个值的生命周期。但这也意味着这个对象必须有一个拷贝构造函数和赋值操作符。
如果Mock函数还需要返回一个值,你可以将SetArgPointee()和Return()放到DoAll()中:
using ::testing::_;
using ::testing::Return;
using ::testing::SetArgPointee;
class MockMutator :public Mutator {
public:
...
MOCK_METHOD1(MutateInt,bool(int* value));
};
...
MockMutator mutator;
EXPECT_CALL(mutator, MutateInt(_))
.WillOnce(DoAll(SetArgPointee<0>(5),
Return(true)));
如果输出参数是一个数组,用SetArrayArgument<N>[first, last)动作。它将源范围[first, last)中的元素拷贝到一个新的以0开始的新数组中:
using ::testing::NotNull;
using ::testing::SetArrayArgument;
classMockArrayMutator :public ArrayMutator {
public:
MOCK_METHOD2(Mutate, void(int* values, int num_values));
...
};
...
MockArrayMutator mutator;
int values[5] = { 1, 2, 3, 4, 5 };
EXPECT_CALL(mutator, Mutate(NotNull(), 5))
.WillOnce(SetArrayArgument<0>(values, values + 5));
当参数是一个输出迭代器时也是可以工作的:
using ::testing::_;
using ::testing::SeArrayArgument;
classMockRolodex :public Rolodex {
public:
MOCK_METHOD1(GetNames,void(std::back_insert_iterator<vector<string> >));
...
};
...
MockRolodex rolodex;
vector<string> names;
names.push_back("George");
names.push_back("John");
names.push_back("Thomas");
EXPECT_CALL(rolodex, GetNames(_))
.WillOnce(SetArrayArgument<0>(names.begin(), names.end()));
如果你期望一个调用改变mock对象的行为,你可以用::testing::InSequence来指定在这个调用之前和之后的对象行为:
using ::testing::InSequence;
using ::testing::Return;
...
{
InSequence seq;
EXPECT_CALL(my_mock, IsDirty())
.WillRepeatedly(Return(true));
EXPECT_CALL(my_mock, Flush());
EXPECT_CALL(my_mock, IsDirty())
.WillRepeatedly(Return(false));
}
my_mock.FlushIfDirty();
这可以让my_mock.IsDirty()在my_mock.Flush()调用之前返回true,而在之后返回false。
如果要改变的对象动作更复杂,你可以保存保存这些效果到一个变量中,并使一个Mock函数从这个变量中得到它的返回值:
using ::testing::_;
using ::testing::SaveArg;
using ::testing::Return;
ACTION_P(ReturnPointee, p) {return *p; }
...
int previous_value = 0;
EXPECT_CALL(my_mock, GetPrevValue())
.WillRepeatedly(ReturnPointee(&previous_value));
EXPECT_CALL(my_mock, UpdateValue(_))
.WillRepeatedly(SaveArg<0>(&previous_value));
my_mock.DoSomethingToUpdateValue();
这样,m_mock.GetPrevValue()总是会返回上一次UpdateValue调用的参数值。
如果一个Mock函数返回类型是一个内置的C++类型或是指针,当它调用时默认会返回0。如果默认值不适合你,你只需要指定一个动作。
有时,你也许想改变默认值,或者你想指定一个Google Mock不知道的类型的默认值。你可以用::testing::DefaultValue类模板:
classMockFoo :public Foo {
public:
MOCK_METHOD0(CalculateBar, Bar());
};
...
Bar default_bar;
// Sets the default return value for type Bar.
DefaultValue<Bar>::Set(default_bar);
MockFoo foo;
// We don't need to specify an action here, as the default
// return value works for us.
EXPECT_CALL(foo, CalculateBar());
foo.CalculateBar(); // This should return default_bar.
// Unsets the default return value.
DefaultValue<Bar>::Clear();
请注意改变一个类型的默认值会让你的测试难于理解。我们建议你谨慎地使用这个特性。比如,你最好确保你在使用这个特性代码之前之后要加上Set()和Clear()调用。
如果你掌握了如何改变一个类型的默认值。但是也许这对于你也许是不够的:也许你有两个Mock函数,它们有相同的返回类型,并且你想它们有不同的行为。ON_CALL()宏允许你在函数级别自定义你的Mock函数行为:
using ::testing::_;
using ::testing::AnyNumber;
using ::testing::Gt;
using ::testing::Return;
...
ON_CALL(foo, Sign(_))
.WillByDefault(Return(-1));
ON_CALL(foo, Sign(0))
.WillByDefault(Return(0));
ON_CALL(foo, Sign(Gt(0)))
.WillByDefault(Return(1));
EXPECT_CALL(foo, Sign(_))
.Times(AnyNumber());
foo.Sign(5); // This should return 1.
foo.Sign(-9); // This should return -1.
foo.Sign(0); // This should return 0.
正如你所猜测的,当有多个ON_CALL()语句时,新的语句(即后写的语句 )会优先匹配。换言之,最后一个匹配参数的Mock函数会被调用。这种匹配顺序允许你开始设置比较宽松的行为,然后再指定这个Mock函数更具体的行为。
如果内置动作不适合你,你可以轻松地用一个已有的函数,方法,仿函数作为一个动作:
using ::testing::_;
using ::testing::Invoke;
class MockFoo :public Foo {
public:
MOCK_METHOD2(Sum, int(int x, int y));
MOCK_METHOD1(ComplexJob, bool(int x));
};
intCalculateSum(int x,int y) {return x + y; }
classHelper {
public:
boolComplexJob(int x);
};
...
MockFoo foo;
Helper helper;
EXPECT_CALL(foo, Sum(_, _))
.WillOnce(Invoke(CalculateSum));
EXPECT_CALL(foo, ComplexJob(_))
.WillOnce(Invoke(&helper, &Helper::ComplexJob));
foo.Sum(5, 6); // Invokes CalculateSum(5, 6).
foo.ComplexJob(10); // Invokes helper.ComplexJob(10);
唯一的要求是这些函数,方法,仿函数的类型必须与Mock函数兼容,即后者的参数必须可以隐式转换成Mock函数中相应的参数,前者的返回值可以隐式转换成Mock函数的返回类型。所以,你可以调用一个与Mock函数定义不完全一致的函数,只要这样做是安全的,精彩吧,huh?
Invoke()在做一些比较复杂的动作时非常有用。它将Mock函数的参数传递给被调用的函数或是仿函数,即被调函数有完整的上下文。如果被调函数或仿函数对其中一些或全部参数不感兴趣,它可以简单地忽略它们。
但一个单元测试者通常想调用一个不带任何一个Mock函数参数的函数。Invoke允许你使用一个包装函数丢弃所有的参数。不消说,这种工作是无趣和并将测试意图晦涩化。
InvokeWithoutArgs()是用来解决这个问题的。它类似Invoke(),只是它不需要将递Mock函数的参数给被调者。下面是一个例子:
using ::testing::_;
using ::testing::InvokeWithoutArgs;
classMockFoo :public Foo {
public:
MOCK_METHOD1(ComplexJob, bool(int n));
};
boolJob1() { ... }
...
MockFoo foo;
EXPECT_CALL(foo, ComplexJob(_))
.WillOnce(InvokeWithoutArgs(Job1));
foo.ComplexJob(10); // Invokes Job1().
有时一个Mock函数会接收一个函数指针或是一个仿函数(换言之,一个”callable” )参数,比如:
classMockFoo :public Foo {
public:
MOCK_METHOD2(DoThis, bool(int n, bool (*fp)(int)));
};
你也许想调用这个函数指针参数:
using ::testing::_;
...
MockFoo foo;
EXPECT_CALL(foo, DoThis(_, _))
.WillOnce(...);
// Will execute (*fp)(5), where fp is the
// second argument DoThis() receives.
啊,你需要引用一个Mock函数的参数,但C++还没有lambda表示式,所以你需要定义你自己的动作。:-(或是你真需要这么做吗?
嗯,Google Mock有一个动作特地来解决这个问题:
InvokeArgument<N>(arg_1, arg_2, ..., arg_m)
它会调用Mock函数接收到的第N个参数,并将arg_1, arg2, ..., arg_m作为参数。无论参数是一个函数指针或是一个仿函数,Google Mock都可以处理。
使用它,你可以写:
using ::testing::_;
using ::testing::InvokeArgument;
...
EXPECT_CALL(foo, DoThis(_, _))
.WillOnce(InvokeArgument<1>(5));
// Will execute (*fp)(5), where fp is the
// second argument DoThis() receives.
如果一个函数是有一个参数是引用呢?没问题,把它放到ByRef()中:
...
MOCK_METHOD1(Bar, bool(bool (*fp)(int,const Helper&)));
...
using ::testing::_;
using ::testing::ByRef;
using ::testing::InvokeArgument;
...
MockFoo foo;
Helper helper;
...
EXPECT_CALL(foo, Bar(_))
.WillOnce(InvokeArgument<0>(5, ByRef(helper)));
// ByRef(helper) guarantees that a reference to helper, not a copy of it,
// will be passed to the callable.
如果函数指针接收的是引用参数,但我们没有将参数放到ByRef()中呢?那么InvokeArgument()会拷贝这个参数,将传递拷贝后的值的引用给函数指针,而不是原来值的引用。这在参数是一个临时变量时特别方便:
...
MOCK_METHOD1(DoThat, bool(bool (*f)(const double& x, const string& s)));
...
MOCK_METHOD1(DoThat,bool(bool (*f)(constdouble& x,const string& s)));
...
using ::testing::_;
using ::testing::InvokeArgument;
...
MockFoo foo;
...
EXPECT_CALL(foo, DoThat(_))
.WillOnce(InvokeArgument<0>(5.0, string("Hi")));
// Will execute (*f)(5.0, string("Hi")), where f is the function pointer
// DoThat() receives. Note that the values 5.0 and string("Hi") are
// temporary and dead once the EXPECT_CALL() statement finishes. Yet
// it's fine to perform this action later, since a copy of the values
// are kept inside the InvokeArgument action.
有时你有一个返回值的动作,但你需要一个返回void的动作(也许你想在一个返回void的Mock函数中用它,或是它在DoAll()中要用它,但它不是DoAll()中最后一个 )。IgnoreResult()允许你实现这个功能。比如:
using ::testing::_;
using ::testing::Invoke;
using ::testing::Return;
intProcess(const MyData& data);
stringDoSomething();
classMockFoo :public Foo {
public:
MOCK_METHOD1(Abc, void(const MyData& data));
MOCK_METHOD0(Xyz, bool());
};
...
MockFoo foo;
EXPECT_CALL(foo, Abc(_))
// .WillOnce(Invoke(Process));
// The above line won't compile as Process() returns int but Abc() needs
// to return void.
.WillOnce(IgnoreResult(Invoke(Process)));
EXPECT_CALL(foo, Xyz())
.WillOnce(DoAll(IgnoreResult(Invoke(DoSomething)),
// Ignores the string DoSomething() returns.
Return(true)));
注意你不能将IgnoreResult()用在一个已经是返回void的动作上。如果你这样做,你会得到一个丑陋的编译错误。
假使你有一个Mock函数Foo(),它接受七个参数,并且你想在Foo调用时使用一个自定义的动作。但问题是,这个自定义的动作只有三个参数:
using ::testing::_;
using ::testing::Invoke;
...
MOCK_METHOD7(Foo,bool(bool visible, const string& name,int x,int y,
const map<pair<int,int>,double>& weight,
double min_weight,double max_wight));
...
bool IsVisibleInQuadrant1(bool visible, int x,int y) {
return visible && x >= 0 && y >= 0;
}
...
EXPECT_CALL(mock, Foo(_, _, _, _, _, _, _))
.WillOnce(Invoke(IsVisibleInQuadrant1)); // Uh, won't compile. :-(
为了取悦编译器,你可以定义一个配接器,它有着与Foo()相同的定义,然后用它调用自定义动作:
using ::testing::_;
using ::testing::Invoke;
boolMyIsVisibleInQuadrant1(bool visible,const string& name,int x,int y,
const map<pair<int,int>,double>& weight,
double min_weight,double max_wight) {
return IsVisibleInQuadrant1(visible, x, y);
}
...
EXPECT_CALL(mock, Foo(_, _, _, _, _, _, _))
.WillOnce(Invoke(MyIsVisibleInQuadrant1)); // Now it works.
但这要写不笨拙吗?
Google Mock提供了一个通用的动作配接器,所以你可以把时间用到更重要的事情上去,而不是写你自己的配接器。下面是它的语法:
WithArgs<N1, N2, ..., Nk>(action)
它创建一个动作,将Mock函数的参数传给内部的动作,用WithArgs,我们前面的例子可以写为:
using ::testing::_;
using ::testing::Invoke;
using ::testing::WithArgs;
...
EXPECT_CALL(mock, Foo(_, _, _, _, _, _, _))
.WillOnce(WithArgs<0, 2, 3>(Invoke(IsVisibleInQuadrant1)));
// No need to define your own adaptor.
为了更好的可读性,Google Mock提供给你了:
l WithoutArgs(action)当内部动作不接受参数
l WithArg<N>(action)(在Arg后没有s )当内部动作接受一个参数。
正如你所认识到的,InvokeWithoutArgs(...)只是WithoutArgs(Invoke(...))的语法糖。
这里有几个小提示:
l 在WithArgs内部的动作并不一定要是Invoke(),它可以是任意的。
l 在参数列表中的参数可以重复的,比如WithArgs<2,3,3,5>(...)。
l 你可以改变参数的顺序,比如WithArgs<3, 2, 1>(...)。
l 所选的参数类型并不一定要完全匹配内部动作的定义。只要它们可以隐式地被转换成内部动作的相应参数就可以了。例如,如果Mock函数的第4个参数是int,而my_action接受一个double参数,WithArg<4>(my_action)可以工作。
Selecting-an-action’s-arguments中介绍了一种使参数不匹配的动作和Mock函数结合使用的方法。但这种方法的缺点是要将动作封装到WithArgs<...>()中,这会使测试者感到麻烦。
如果你定义要用于Invoke*的一个函数,方法,或是仿函数,并且你对它的一些函数不感兴趣,另一种做法是声明你不感兴趣的参数为Unused。这会吏定义更清爽,并在不感兴趣的参数发生变化时更健壮。而且它可以增加一个动作函数被重用的可能性。比如,有:
MOCK_METHOD3(Foo,double(const string& label, double x,double y));
MOCK_METHOD3(Bar,double(int index, double x,double y));
你除了可以像下面一样写:
using ::testing::_;
using ::testing::Invoke;
doubleDistanceToOriginWithLabel(const string& label,double x,double y) {
return sqrt(x*x + y*y);
}
doubleDistanceToOriginWithIndex(int index,double x,double y) {
return sqrt(x*x + y*y);
}
...
EXEPCT_CALL(mock, Foo("abc", _, _))
.WillOnce(Invoke(DistanceToOriginWithLabel));
EXEPCT_CALL(mock, Bar(5, _, _))
.WillOnce(Invoke(DistanceToOriginWithIndex));
你还可以写:
using ::testing::_;
using ::testing::Invoke;
using ::testing::Unused;
doubleDistanceToOrigin(Unused, double x,double y) {
return sqrt(x*x + y*y);
}
...
EXEPCT_CALL(mock, Foo("abc", _, _))
.WillOnce(Invoke(DistanceToOrigin));
EXEPCT_CALL(mock, Bar(5, _, _))
.WillOnce(Invoke(DistanceToOrigin));
如匹配器一样,Google Mock动作对象中也有一个指针指向引用计数的实现对象。所以拷贝动作是允许的并且也是高效的。当最后一个引用实现对象的动作死亡后,现实对象会被delete。
如果你有一些想重复使用的复杂动作。你也许不想每次都重新产生一次。如果这个动作没有一个内部状态(比如:它在每次调用都做相同的事),你可以将它赋值给一个动作变量,以后就可以重复使用这个变量了,比如:
Action<bool(int*)> set_flag = DoAll(SetArgPointee<0>(5),
Return(true));
... use set_flag in .WillOnce()and .WillRepeatedly() ...
但是,如果一个动作有自己的状态,那你共享这个动作对象时,你也许会得到一些意外的结果。假设你有一个动作工厂IncrementCounter(init),它创建一个动作,这个动作中的计数器初始值是init,每次调用增加计数器的值并返回计数器值,使用从相同的语句中产生的两个动作和使用一个共享动作会产生不同的行为。比如:
EXPECT_CALL(foo, DoThis())
.WillRepeatedly(IncrementCounter(0));
EXPECT_CALL(foo, DoThat())
.WillRepeatedly(IncrementCounter(0));
foo.DoThis(); // Returns 1.
foo.DoThis(); // Returns 2.
foo.DoThat(); // Returns 1 - Blah() uses a different
// counter than Bar()'s.
相较:
Action<int()> increment = IncrementCounter(0);
EXPECT_CALL(foo, DoThis())
.WillRepeatedly(increment);
EXPECT_CALL(foo, DoThat())
.WillRepeatedly(increment);
foo.DoThis(); // Returns 1.
foo.DoThis(); // Returns 2.
foo.DoThat(); // Returns 3 - the counter is shared.
无论你相信与否,编译一个Mock类的大部分时间都花费在产生它的构造函数和析构函数上了,因为它们要做很多的任务(比如,对期望的验证)。更严重的是,有不同函数声明的Mock函数,它们的构造函数/析构函数需要由编译器分别产生,所以,如果你Mock许多不同类型的函数,编译你的Mock类会非常慢。
如果你现在发现编译很慢,你可以将Mock类的构造函数/析构函数移出Mock类,将它们放到.cpp文件中。这样做后,即使你在多个文件中#include你的Mock文件,编译器只用产生一次constructor和destructor,这样做编译的更快。
让我们以一个例子说明一下,下面是一个原有的Mock类:
// File mock_foo.h.
...
class MockFoo :public Foo {
public:
// Since we don't declare the constructor or the destructor,
// the compiler will generate them in every translation unit
// where this mock class is used.
MOCK_METHOD0(DoThis, int());
MOCK_METHOD1(DoThat, bool(constchar* str));
... more mock methods ...
};
修改后,变为:
// File mock_foo.h.
...
class MockFoo :public Foo {
public:
// The constructor and destructor are declared, but not defined, here.
MockFoo();
virtual ~MockFoo();
MOCK_METHOD0(DoThis, int());
MOCK_METHOD1(DoThat, bool(constchar* str));
... more mock methods ...
};
和:
// File mock_foo.cpp.
#include"path/to/mock_foo.h"
// The definitions may appear trivial, but the functions actually do a
// lot of things through the constructors/destructors of the member
// variables used to implement the mock methods.
MockFoo::MockFoo() {}
MockFoo::~MockFoo() {}
当你的Mock对象销毁的时候,它会自动检查所有的期望是否满足,如果没满足,会产生一个Google Test失败。这种方式让你可以少去操心一件事。但是如果你不确定你的Mock对象是否会被销毁时,你还是要操心了。
一个Mock对象怎么会最终没有被销毁呢?嗯,它可以是在由被测试的代码在堆上分配的。假设代码中有一个bug,它没能正常地delete Mock对象,你最终可能会在测试中有bug时,让测试通过。
使用一个堆检查工具是一个好主意,可以减少了一些担心,但它的实现不是100%可靠的。所以有时你想在一个Mock对象(希望如些 )销毁前,强制Google Mock去检查它。你可以写:
TEST(MyServerTest, ProcessesRequest) {
using ::testing::Mock;
MockFoo* const foo =new MockFoo;
EXPECT_CALL(*foo, ...)...;
// ... other expectations ...
// server now owns foo.
MyServer server(foo);
server.ProcessRequest(...);
// In case that server's destructor will forget to delete foo,
// this will verify the expectations anyway.
Mock::VerifyAndClearExpectations(foo);
} // server is destroyed when it goes out of scope here.
提示:Mock::VerifyAndClearExpectations()函数返回一个bool值来标明检查是否成功(成功为true),所以你可以将函数放到ASSERT_TRUE()中,如果这个断言失败,就没有必要再继续了。
有时你也许也在多个检查点”重置”一个Mock对象:在每个检查点,你可以检查在这个Mock对象上的所有设置的期望是否满足,并且你可以设置在它上面设置一些新的期望,就如同这个Mock对象是新创建的一样。这样做可以让你让你的测试”分段”地使用Mock对象。
其中一个使用场景是在你的测试的SetUp()函数中,你也许想借助Mock对象,将你测试的对象放到一个特定的状态。如果在一个合适的状态后,你清除所在Mock对象上的所有期望,这样你可以在TEST_F中设置新的期望。
正如你也许会发现的一样,Mock::VerifyAndClearExpectations()函数会在这帮助你。或是如果你正用ON_CALL()设置在这个Mock对象上的默认动作,并且想清除这个Mock对象上的默认动作,就用Mock::VerifyAndClear(&mock_object)。这个函数会做如Mock::VerifyAndClearExpectations(&mock_object)相同的工作,并返回相同的bool值,但它还会清除设置在mock_object上设置的ON_CALL()语句。
另一个可以达到相同效果的技巧是将期望放到序列(sequence)中,在指定的位置将调用放到无效果的(dummy)检查点函数中。然后你可以检查Mock函数在指定时间的行为了。比如,你有下面的代码:
Foo(1);
Foo(2);
Foo(3);
你想验证Foo(1)和Foo(3)都调用了mock.Bar(“a”),但Foo(2)没有调用任何函数,你可以写:
using ::testing::MockFunction;
TEST(FooTest, InvokesBarCorrectly) {
MyMock mock;
// Class MockFunction<F> has exactly one mock method. It is named
// Call() and has type F.
MockFunction<void(string check_point_name)> check;
{
InSequence s;
EXPECT_CALL(mock, Bar("a"));
EXPECT_CALL(check, Call("1"));
EXPECT_CALL(check, Call("2"));
EXPECT_CALL(mock, Bar("a"));
}
Foo(1);
check.Call("1");
Foo(2);
check.Call("2");
Foo(3);
}
期望指明了第一个Bar(“a”)必须在检查点”1”之前发生,第二个Bar(“a”)必须在检查点”2”之后发生,并且两个检查点之间不应该发生任何事。这种使用检查点的明确写法很容易指明哪个Foo函数调用的Bar(“a”)。
有时你想明确一个Mock对象在指定时间销毁,比如,在bar->A()调用之后,但在bar->()调用之前。我们已经知道可以指定Mock函数调用的顺序,所以我们需要做的是Mock所Mock函数的析构函数。
这听起来简单,但一个问题:析构函数是一个特殊的函数,它有着特殊的语法和语义,MOCK_METHOD0对它是无效的:
MOCK_METHOD0(~MockFoo, void()); // Won't compile!
好消息是你可以用一个简单的模式来达到相同的效果。首先,在你的Mock类中添加一个Mock函数Die(),并在析构函数中调用它,如下:
classMockFoo :public Foo {
...
// Add the following two lines to the mock class.
MOCK_METHOD0(Die, void());
virtual~MockFoo() { Die(); }
};
(如果Die()与已有符号冲突,选另一个名字),现在,我们将测试一个MockFoo对象析构时的问题,转化为测试当Die函数被调用时的问题了:
MockFoo* foo =new MockFoo;
MockBar* bar =new MockBar;
...
{
InSequence s;
// Expects *foo to die after bar->A() and before bar->B().
EXPECT_CALL(*bar, A());
EXPECT_CALL(*foo, Die());
EXPECT_CALL(*bar, B());
}
重要提示:我们这节所描述的只有在Google Mock是线程安全的平台上才是成立的。现在只有支持pthreads库的平台(这包括Linux和Mac)才可以。要让它在其它平台上线程安全,我们只需要在”gtest/internal/gtest-port.h”中实现一些同步操作。
在一个单元测试中,如果你可以将一块代码独立出来在单线程环境中测试是最好的。这可以防止竞争和死锁,并使你debug你的测试容易的多。
但许多程序是多线程的,并有时我们需要在多线程环境中测试它,Google Mock也提供了这个功能。
回忆使用一个Mock的步骤:
1. 创建一个Mock对象foo。
2. 用ON_CALL()和EXPECT_CALL()设置它的默认行为和期望。
3. 测试调用foo的函数的代码。
4. 可选的,检查和重置mock。
5. 你自己销毁mock,或是让测试代码销毁mock,析构函数会自动检查是否满足。
如果你能遵循下面的简单规则,你的Mock和线程可以幸福地生活在一起:
l 在单线程中执行你的测试代码(相对于被测试代码)。这可以保证你的测试容易被跟踪。
l 显然,你可以在第一步中不用锁。
l 当你做第2步和第5步,要保证没有其它线程访问foo。很显然,不是吗?
l 第3步和第4步可以在单线程或是多线程中进行—随便你。Google Mock会去处理锁,所以你不需要再做什么事,除非是你测试需要。
如果你违反了这些规则(比如,如果你在其它线程正在访问的一个Mock上测试了期望),你会得到一个不确定的行为。这不会是什么令你开心的事,所以别去尝试。
Google Mock保证一个Mock函数的动作会在调用这个Mock的线程中进行。比如:
EXPECT_CALL(mock, Foo(1))
.WillOnce(action1);
EXPECT_CALL(mock, Foo(2))
.WillOnce(action2);
如果在线程1中被调用Foo(1),且在线程2中调用Foo(2),Google Mock会在线程1中执行action1,在线程2中执行action2。
Google Mock不会对多个线程设置动作的顺序(这样做可能会产生死锁,因为动作可能需要配合 )。这意味着在上面的例子中执行action1和action2可能会交错。如果这样不符合你的意思,你应该对action1和action2加入同步逻辑,来保证测试线程安全。
同样,要记得DefaultValue<T>是一个全局资源,所以它会影响你程序中的所有活动的Mock对象。很自然的,你不会想在多线程环境中与它纠缠不清,或是你在Mock对象的动作正在进行时,你去使用DefaultValue。
当Google Mock看到有潜在的错误时(比如,一个没有设置期望的Mock函数被调用了 ),它会打印一些警告信息,包括这个函数的参数和返回值,并希望你可以提醒你关注一下,看这里是不是的确是一个错误。
有时你对你的测试正确性比较自信,也许就不喜欢这些友好的提示。有的时候,你在debug你的测试,并在推敲你所测试的代码的行为,那你也许会希望看到每个Mock函数被调用的信息(包括参数值和返回值 )。很明显,一种打印级别是不是满足所有需求的。
你可以通过—gmock_verbose=LEVEL命令参数来设置打印级别,其中LEVEL是一个有三种取值的字符串。
l info:Google Mock会打印所有的信息,包括正常的消息,警告,错误。在这种级别设置上,Google Mock会记录所有对于ON_CALL/EXPECT_CALL的调用。
l warning:Google Mock会打印警告和错误信息,这是默认的。
l error:Google仅会打印错误。
另外,你可以在你的测试中设置打印级别,如:
::testing::FLAGS_gmock_verbose ="error";
现在,请明智地选择打印级别,让Google Mock更好地为你服务。
如果你在Emacs中运行你的测试,错误相关的Google Mock和Google Test源文件位置会被高亮标记。只用其中一行上敲回车就会跳到相应的行。或是你可以敲C-x `跳到下一个错误。
为了操作更简单,你可以将加下面几行加入~/.emacs文件中:
(global-set-key "\M-m" 'compile) ; m is for make
(global-set-key [M-down] 'next-error)
(global-set-key [M-up] '(lambda () (interactive) (next-error -1)))
然后你可以敲M-m编译,或是用M-up/M-down在错误提示中上下移动。
Google Mock的实现包括几十个文件(包括它自己的测试 )。有时你也许将它们放到几个文件中,这样你就可以更容易地把它们拷贝到一个新机器上。对于这个需求,我们提供了Python脚本fuse_gmock_files.py,它在srcipts/目录下。假设你已经安装了Python 2.4或更高的版本,你进入目录后运行:
Python fuse_gmock_files.py OUTPUT_DIR
你应该会看到OUTPUT_DIR被创建了,并且里面有gtest/gtest.h,gmock/gmock.h和gmock-gtest-all.cc。这三个文件包含你需要使用Google Mock( 和Google Test )的所有东西。只需要把它们拷贝到任何地方,然后你就可以开始用Mock写测试了。你可以用sripts/test/Makefile文件作为一个编译你的测试的例子。
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)
returntrue;
*result_listener << "the remainder is " << (arg % 7);
returnfalse;
}
有了这个定义,上面断言会给出一个更好的提示:
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的状态
有时你想定义有一个有参数的匹配器。对于这个要求你可以使用宏:
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));
这会得到下面的信息(假设n是10):
Value of: Blah("a")
Expected: has absolute value 10
Actual: -9
注意匹配器的描述和它的参数都被打印了,使信息更友好。
在匹配器定义内,你可以写foo_type来引用一个名为foo的参数类型。比如在上例MATCHER_P(HasAbsoluteValue, Value)中,你可以用value_type来引用value的类型。
Google Mock还提供MATCHER_P2,MATCHER_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()来定义(下面会介绍),特别是你会经常用这个匹配器的时候。尽管这些方法会花费更多的力气,但它们会给你更多的控制能力:可以控制匹配值的类型,匹配器参数,这样一般也会产生更好的编译提示信息,用这些方法以长远的目光来看是更好的选择。它们还允许重载不同参数类型(而不是仅能通过不同参数个数重载)。
一个实现了::testing::MatcherInterface<T>的T参数类型的匹配器可以做两种事:它判断参数T是否匹配匹配器,并可以描述它所匹配的类型。后一种能力可以在期望失败时给出可读的错误信息。
classMatchResultListener {
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 <typenameT>
classMatcherInterface {
public:
virtual~MatcherInterface();
// Returns true iff the matcher matches x; also explains the match
// result to 'listener'.
virtualboolMatchAndExplain(T x,MatchResultListener* listener)const = 0;
// Describes this matcher to an ostream.
virtualvoidDescribeTo(::std::ostream* os)const = 0;
// Describes the negation of this matcher to an ostream.
virtualvoidDescribeNegationTo(::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 :publicMatcherInterface<int> {
public:
virtualboolMatchAndExplain(int n,MatchResultListener* listener)const {
return (n % 7) == 0;
}
virtualvoidDescribeTo(::std::ostream* os)const {
*os << "is divisible by 7";
}
virtualvoidDescribeNegationTo(::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 :publicMatcherInterface<int> {
public:
virtualboolMatchAndExplain(int n,
MatchResultListener* listener)const {
constint 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)
在前一节中的你了解了如何去写自己的匹配器。只有还有一个问题:一个用MakeMatcher()创建的匹配器只能在参数类型确定的情况下用。如果你想要一个多形的(接受多种类型)匹配器(比如,Eq(x)可以匹配任何value==x的value,value和x不一定是同一类型),你可以通过”gmock/gmock-matchers.h”学习这个技巧,但这又要了解太多。
幸运的是,大多数时间你在MakePolymorphicMatcher()的帮助下,很容易定义一个多形匹配器。下面是以定义NotNull()为例:
using ::testing::MakePolymorphicMatcher;
using ::testing::MatchResultListener;
using ::testing::NotNull;
using ::testing::PolymorphicMatcher;
classNotNullMatcher {
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 <typenameT>
boolMatchAndExplain(T* p,
MatchResultListener* /* listener */)const {
return p != NULL;
}
// Describes the property of a value matching this matcher.
voidDescribeTo(::std::ostream* os)const { *os <<"is not NULL"; }
// Describes the property of a value NOT matching this matcher.
voidDescribeNegationTo(::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参数中。
在Times()中的Cardinality用于告诉Google Mock调用发生多少次。它不必是准确的。比如,你可以说AtLeast(5)或Between(2,4)。
如果内置的cardinality集合不适合你,你可以通过下面的接口定义你自己的cardinality。
classCardinalityInterface {
public:
virtual~CardinalityInterface();
// Returns true iff call_count calls will satisfy this cardinality.
virtualboolIsSatisfiedByCallCount(int call_count)const = 0;
// Returns true iff call_count calls will saturate this cardinality.
virtualboolIsSaturatedByCallCount(int call_count)const = 0;
// Describes self to an ostream.
virtualvoidDescribeTo(::std::ostream* os)const = 0;
};
比如,指定一个调用必须发生偶数次,你可以写:
using ::testing::Cardinality;
using ::testing::CardinalityInterface;
using ::testing::MakeCardinality;
classEvenNumberCardinality :publicCardinalityInterface {
public:
virtualboolIsSatisfiedByCallCount(int call_count)const {
return (call_count % 2) == 0;
}
virtualboolIsSaturatedByCallCount(int call_count)const {
returnfalse;
}
virtualvoidDescribeTo(::std::ostream* os)const {
*os << "called even number of times";
}
};
CardinalityEvenNumber() {
return MakeCardinality(new EvenNumberCardinality);
}
...
EXPECT_CALL(foo, Bar(3));
如果内置的动作不适合你,并且发现用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函数的类型 |
比如,用ACTION为MOCK函数作为一个stub动作:
intDoSomething(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*) |
有时你想参数化你定义的一个动作。对此我们有另一个宏:
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_P2,ACTION_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) { ... }
为了最大化简洁性和可重用性,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<constchar*, arg1_type>();
// Makes sure param can be converted to bool.
bool flag = param;
}
其中StaticAssertTypeEq在Google Test中一个编译期判断两个类型是否匹配的断言。
有时你想给一个动作明确的模板参数,而不是由编译器推导出参数类型。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取值在1到10,n取值在0到10。name_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, unsignedchar>(&n));
如果你想明确地指明参数类型,你可以提供更多的模板参数:
ActionName<t1, ..., t_m, u1, ..., u_k>(v1, ..., v_n)
其中u_i是v_i参数所期望的类型。
ACTION_TEMPLATE和ACTION/ACTION_P*可以通过参数个数重载,但不能通过模板参数个数重载。如果没有这个限制,下面的代码含义就不明确了:
OverloadedAction<int,bool>(x);
我们是在用一个模板参数的功能,其中bool是指x的类型呢?或是两个板板参数,其中编译器需要推出x的类型。
如果你写一个返回一个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> |
... |
... |
... |
注意,我们要选择不同的前缀(Action,ActionP,ActionP2,等等)用于区别有不同参数个数的动作,否则不能通过参数个数重载的动作。
虽然ACTION*宏很方便,但有时它们是不合适的。比如,在前一节中介绍的技巧,不能让你直接指定Mock函数参数和动作参数,这通常会引发一些没有优化的错误信息,这又会困扰一些不熟悉此的用户。实现根据参数类型重载动作,需要越过重重障碍。
另一个方法是实现::testing::ActionInterface<F>,其中F是用于动作的Mock函数的类型。比如:
template <typenameF>classActionInterface {
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 ResultPerform(const ArgumentTuple& args) = 0;
};
using ::testing::_;
using ::testing::Action;
using ::testing::ActionInterface;
using ::testing::MakeAction;
typedefintIncrementMethod(int*);
classIncrementArgumentAction :publicActionInterface<IncrementMethod> {
public:
virtualintPerform(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.
上一节中介绍了如何定义自己的动作。这是不错,除了你需要知道动作中要用的函数类型这一点。有时这会是一个问题。比如,如果你想用在动作中用不同类型的函数(比如Return()和SetArgPointee())。
如果一个动作可以用在不同类型的Mock函数中,我们就称它是多形的。MakePolymorphicActions()函数模板让这种定义很容易。
namespace testing {
template <typenameImpl>
PolymorphicAction<Impl>MakePolymorphicAction(constImpl& impl);
} // namespace testing
举一例子,我们定义一个返回Mock函数参数列表中第二个参数的动作。第一步是定义一个实现类:
classReturnSecondArgumentAction {
public:
template <typenameResult,typenameArgumentTuple>
ResultPerform(constArgumentTuple& 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),其中R是Mock函数的返回类型,args是它的参数以元组形式的表示。
接下来,我们用MakePolymorphicAction()将这个实现类对象变为我们所需的多形动作。它封装成下面这种形式会很方便:
using ::testing::MakePolymorphicAction;
using ::testing::PolymorphicAction;
PolymorphicAction<ReturnSecondArgumentAction>ReturnSecondArgument() {
return MakePolymorphicAction(ReturnSecondArgumentAction());
}
现在,你可以像用内置动作一样的方式来用这个多形的动作:
using ::testing::_;
classMockFoo :public Foo {
public:
MOCK_METHOD2(DoThis, int(bool flag, int n));
MOCK_METHOD3(DoThat, string(int x,constchar* str1,constchar* 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".
当一个未设置或是未期望的调用发生时,Google Mock会打印参数值和栈trace帮你debug。像EXPECT_THAT和EXPECT_EQ这些断言宏会在断言失败时打印这些值。Google Mock和Google Test会用Google Test的用户可扩展值打印器。
这个打印器知道如何打印C++内置类型,普通数据,STL容器,和支持<<操作符的任何类型。其它类型,它会以值的原始字符的形式打印,希望可以帮助到你。Google Test的高级指南中解释了如何扩展打印器来打印你自己的类型。