源码分析

通过《Google Mock(Gmock)简单使用和源码分析——简单使用》中的例子,我们发现被mock的相关方法在mock类中已经被重新实现了,否则它们也不会按照我们的期待的行为执行。我们通过阅读源码,来分析整个过程的实现逻辑。(转载请指明出于breaksoftware的csdn博客)

MOCK_METHOD系列宏

首先我们以MOCK_METHOD0为例

#define MOCK_METHOD0(m, ...) GMOCK_METHOD0_(, , , m, __VA_ARGS__)

可以看到它实际上封装了GMOCK_METHOD0_。我们在介绍GMOCK_METHOD0_之前,还可以看到其他无参数的宏

#define MOCK_CONST_METHOD0(m, ...) GMOCK_METHOD0_(, const, , m, __VA_ARGS__)
……
#define MOCK_METHOD0_T(m, ...) GMOCK_METHOD0_(typename, , , m, __VA_ARGS__)
……
#define MOCK_CONST_METHOD0_T(m, ...) \GMOCK_METHOD0_(typename, const, , m, __VA_ARGS__)
……
#define MOCK_METHOD0_WITH_CALLTYPE(ct, m, ...) \GMOCK_METHOD0_(, , ct, m, __VA_ARGS__)
……
#define MOCK_CONST_METHOD0_WITH_CALLTYPE(ct, m, ...) \GMOCK_METHOD0_(, const, ct, m, __VA_ARGS__)
……
#define MOCK_METHOD0_T_WITH_CALLTYPE(ct, m, ...) \GMOCK_METHOD0_(typename, , ct, m, __VA_ARGS__)
……
#define MOCK_CONST_METHOD0_T_WITH_CALLTYPE(ct, m, ...) \GMOCK_METHOD0_(typename, const, ct, m, __VA_ARGS__)
……

这些无参数的宏宏都是基于GMOCK_METHOD0_实现的,它们的差别只是不同参数的组合。这儿要列出它们是因为GMOCK_METHOD0_的定义比较晦涩,通过这些醒目的定义,我们将会发现其各个参数的作用。

// INTERNAL IMPLEMENTATION - DON'T USE IN USER CODE!!!
#define GMOCK_METHOD0_(tn, constness, ct, Method, ...) \GMOCK_RESULT_(tn, __VA_ARGS__) ct Method( \) constness { \GTEST_COMPILE_ASSERT_((::testing::tuple_size<                          \tn ::testing::internal::Function<__VA_ARGS__>::ArgumentTuple>::value \== 0), \this_method_does_not_take_0_arguments); \GMOCK_MOCKER_(0, constness, Method).SetOwnerAndName(this, #Method); \return GMOCK_MOCKER_(0, constness, Method).Invoke(); \} \::testing::MockSpec<__VA_ARGS__>& \gmock_##Method() constness { \GMOCK_MOCKER_(0, constness, Method).RegisterOwner(this); \return GMOCK_MOCKER_(0, constness, Method).With(); \} \mutable ::testing::FunctionMocker<__VA_ARGS__> GMOCK_MOCKER_(0, constness, \Method)

上面宏中的tn可以通过之前的调用发现其应该是typename这类用于定义模板的关键字。constness表示mock的方法是不是const类型的。ct是调用约定,比如我们在windows程序里经常见到的STDMETHODCALLTYPE。Method是被mock的函数名。不定参数则是函数指针类型。这儿比较有意思的是不定参数,因为作为一个框架,它需要支持各种类型的函数,而我们不可能把所有类型一一进行罗列。这个时候我们就可以使用不定参数来解决这个问题。
        我们先总览一下GMOCK_METHOD0_的实现。上述代码第17行定义了一个具有mutable属性的变量,之所以使用mutable是因为它可能会被使用在const类型的函数中,然而该对象的方法并不一定是const的。这个参数的名称使用GMOCK_MOCKER_宏组装

#define GMOCK_MOCKER_(arity, constness, Method) \GTEST_CONCAT_TOKEN_(gmock##constness##arity##_##Method##_, __LINE__)#define GTEST_CONCAT_TOKEN_(foo, bar) GTEST_CONCAT_TOKEN_IMPL_(foo, bar)
#define GTEST_CONCAT_TOKEN_IMPL_(foo, bar) foo ## bar

可以见得该参数名包括了gmock关键字、是否有const属性、参数个数、方法名已经所在的行号组成。这样就尽可能的保证该变量在同一个文件中的唯一性。

该变量的类型是一个以函数类型为模板参数的对象,其模板类的定义是

template <typename R>
class FunctionMocker<R()> : publicinternal::FunctionMockerBase<R()> {public:typedef R F();typedef typename internal::Function<F>::ArgumentTuple ArgumentTuple;MockSpec<F>& With() {return this->current_spec();}R Invoke() {// Even though gcc and MSVC don't enforce it, 'this->' is required// by the C++ standard [14.6.4] here, as the base class type is// dependent on the template argument (and thus shouldn't be// looked into when resolving InvokeWith).return this->InvokeWith(ArgumentTuple());}
};

该模板类定义的模板类型就是函数的返回值类型——R。比如例子中Online方法,它被mock之后,传导到该类的R就是bool。上面代码中05行使用返回类型重新定义了函数类型为F()。06行别名了用于保存函数参数的元组类型为ArgumentTuple。08行定义的With函数是用于对参数的筛选。于是我们是以无参数函数为例,所以该处没有设定参数预期。12行是我们mock函数的真实实现。这些内容我们将在之后详细讲解,我们再回到GMOCK_METHOD0_的定义上

 GMOCK_RESULT_(tn, __VA_ARGS__) ct Method( \) constness { \GTEST_COMPILE_ASSERT_((::testing::tuple_size<                          \tn ::testing::internal::Function<__VA_ARGS__>::ArgumentTuple>::value \== 0), \this_method_does_not_take_0_arguments); \GMOCK_MOCKER_(0, constness, Method).SetOwnerAndName(this, #Method); \return GMOCK_MOCKER_(0, constness, Method).Invoke(); \} \

GMOCK_RESULT_宏定义了mock函数的返回类型

#define GMOCK_RESULT_(tn, ...) \tn ::testing::internal::Function<__VA_ARGS__>::Resulttemplate <typename R>
struct Function<R()> {typedef R Result;typedef ::testing::tuple<> ArgumentTuple;typedef typename MatcherTuple<ArgumentTuple>::type ArgumentMatcherTuple;typedef void MakeResultVoid();typedef IgnoredValue MakeResultIgnoredValue();
};

这么定义的一个优点就是可以通过模板将函数类型的定义中的返回类型给拆出来。这和FunctionMocker定义方式是一样的。
        GTEST_COMPILE_ASSERT_宏用于检测定义的参数个数是否符合规定。检测完之后,使用FunctionMocker模板类对象的SetOwnerAndName方法将对象指针和方法名传递到底层逻辑中。最后就会调用FunctionMocker模板类对象的Invoke方法实现函数行为逻辑的调用。

GMOCK_METHOD0_中还定义了另一个方法

  ::testing::MockSpec<__VA_ARGS__>& \gmock_##Method() constness { \GMOCK_MOCKER_(0, constness, Method).RegisterOwner(this); \return GMOCK_MOCKER_(0, constness, Method).With(); \} \

它使用了gmock和函数名组合成为一个新的函数。该函数内部通过FunctionMocker模板类对象的RegisterOwner方法保存了对象指针,最后返回了MockSpec模板对象。MockSpec模板对象在之前我们见过,它是为了实现参数筛选而设计的。其具体实现我们在之后会分析。

无参数的版本忽略了很多函数参数的问题,但是其让我们可以清晰的看见实现的脉络。现在我们将以有两个参数的版本来讲解其实现。

// INTERNAL IMPLEMENTATION - DON'T USE IN USER CODE!!!
#define GMOCK_METHOD2_(tn, constness, ct, Method, ...) \GMOCK_RESULT_(tn, __VA_ARGS__) ct Method( \GMOCK_ARG_(tn, 1, __VA_ARGS__) gmock_a1, \GMOCK_ARG_(tn, 2, __VA_ARGS__) gmock_a2) constness { \GTEST_COMPILE_ASSERT_((::testing::tuple_size<                          \tn ::testing::internal::Function<__VA_ARGS__>::ArgumentTuple>::value \== 2), \this_method_does_not_take_2_arguments); \GMOCK_MOCKER_(2, constness, Method).SetOwnerAndName(this, #Method); \return GMOCK_MOCKER_(2, constness, Method).Invoke(gmock_a1, gmock_a2); \} \::testing::MockSpec<__VA_ARGS__>& \gmock_##Method(GMOCK_MATCHER_(tn, 1, __VA_ARGS__) gmock_a1, \GMOCK_MATCHER_(tn, 2, __VA_ARGS__) gmock_a2) constness { \GMOCK_MOCKER_(2, constness, Method).RegisterOwner(this); \return GMOCK_MOCKER_(2, constness, Method).With(gmock_a1, gmock_a2); \} \mutable ::testing::FunctionMocker<__VA_ARGS__> GMOCK_MOCKER_(2, constness, \Method)

上例中,我们发现相关函数的定义多了两个参数声明。我们先看和mock函数同名的函数的参数定义,它使用了GMOCK_ARG_宏指定参数类型

#define GMOCK_ARG_(tn, N, ...) \tn ::testing::internal::Function<__VA_ARGS__>::Argument##N

Function模板类在之前我们反复见过,它的一个非常大的作用就是从函数类型中拆分出函数返回值类型和各个参数类型。因为之前以无参数函数为例,所以我们并没有欣赏到它的妙处。

template <typename R, typename A1>
struct Function<R(A1)>: Function<R()> {typedef A1 Argument1;typedef ::testing::tuple<A1> ArgumentTuple;typedef typename MatcherTuple<ArgumentTuple>::type ArgumentMatcherTuple;typedef void MakeResultVoid(A1);typedef IgnoredValue MakeResultIgnoredValue(A1);
};template <typename R, typename A1, typename A2>
struct Function<R(A1, A2)>: Function<R(A1)> {typedef A2 Argument2;typedef ::testing::tuple<A1, A2> ArgumentTuple;typedef typename MatcherTuple<ArgumentTuple>::type ArgumentMatcherTuple;typedef void MakeResultVoid(A1, A2);typedef IgnoredValue MakeResultIgnoredValue(A1, A2);
};

Function模板类的两个参数的版本继承于一个参数的版本,一个参数版本继承于无参数版本。这样两个参数版本中,它从无参数版本中继承到了

typedef R Result;

从一个参数版本中继承到了

typedef A1 Argument1;

而自身定义了

typedef A2 Argument2;

它还覆盖了基类中ArgumentTuple、ArgumentMatcherTuple等的定义。

我们看到两个参数版本的Function类的模板类型是R(A1, A2),这种方式就是函数类型的定义。而R、A1和A2是Function模板类的模板。以Login方法为例

MOCK_METHOD2(Login, bool(const std::string&, const std::string&));

编译器将推导出R是bool,A1和A2都是const  std::string&。这样它便将函数返回类型和参数进行了拆分。并别名了各个类型,从而方便在之后模板中忽略具体类型。
       相应的FunctionMocker也是使用相同的方式实现了拆分,我们看下两个参数版本的实现

template <typename R, typename A1, typename A2>
class FunctionMocker<R(A1, A2)> : publicinternal::FunctionMockerBase<R(A1, A2)> {public:typedef R F(A1, A2);typedef typename internal::Function<F>::ArgumentTuple ArgumentTuple;MockSpec<F>& With(const Matcher<A1>& m1, const Matcher<A2>& m2) {this->current_spec().SetMatchers(::testing::make_tuple(m1, m2));return this->current_spec();}R Invoke(A1 a1, A2 a2) {// Even though gcc and MSVC don't enforce it, 'this->' is required// by the C++ standard [14.6.4] here, as the base class type is// dependent on the template argument (and thus shouldn't be// looked into when resolving InvokeWith).return this->InvokeWith(ArgumentTuple(a1, a2));}
};

相比于无参数版本,它在With函数中使用了SetMatchers方法实现了参数限制,并在Invoke中,使用两个参数定义了一个临时的参数元组类型ArgumentTuple对象。这样将参数放到一个元组对象中,是对InvokeWith方法对不同个数、不同类型、不同顺序的参数调用实现统一化处理。

EXPECT_CALL、ON_CALL宏

在介绍MOCK_METHOD系列宏是,我们发现其在我们mock的类中定义两个方法和一个变量:

  1. GMOCK_RESULT_(tn, __VA_ARGS__) ct Method(……)
  2. ::testing::MockSpec<__VA_ARGS__>& gmock_##Method(……)
  3. mutable ::testing::FunctionMocker<__VA_ARGS__> GMOCK_MOCKER_(0, constness, Method)

1中的方法和我们希望mock的方法同名,这将方便使用者调用它。2中的函数是使用gmock和函数名联合组装成的新函数名,它返回了一个被参数筛选的函数对象。EXPECT_CALL和ON_CALL宏中就是调用了它。

#define GMOCK_ON_CALL_IMPL_(obj, call) \((obj).gmock_##call).InternalDefaultActionSetAt(__FILE__, __LINE__, \#obj, #call)
#define ON_CALL(obj, call) GMOCK_ON_CALL_IMPL_(obj, call)#define GMOCK_EXPECT_CALL_IMPL_(obj, call) \((obj).gmock_##call).InternalExpectedAt(__FILE__, __LINE__, #obj, #call)
#define EXPECT_CALL(obj, call) GMOCK_EXPECT_CALL_IMPL_(obj, call)

宏中((obj).gmock_##call)就是调用了2中的方法,并对返回的对象调用InternalDefaultActionSetAt或InternalExpectedAt。以下面的调用为例

EXPECT_CALL(test_user, Pay(_)).WillRepeatedly(testing::Return(true));

其最终是这样的调用

test_user.gmock_Pay(_).InternalExpectedAt(__FILE__, __LINE__, 'test_user', 'Pay').WillRepeatedly(testing::Return(true));

下划线_是通配符,它的定义如下

const internal::AnythingMatcher _ = {};class AnythingMatcher {public:template <typename T>operator Matcher<T>() const { return A<T>(); }
};template <typename T>
inline Matcher<T> A() { return MakeMatcher(new internal::AnyMatcherImpl<T>()); }template <typename T>
inline Matcher<T> MakeMatcher(const MatcherInterface<T>* impl) {return Matcher<T>(impl);
}

为什么任何函数的参数都可以接受AnythingMatcher。我们可以见2中参数的定义

  ::testing::MockSpec<__VA_ARGS__>& \gmock_##Method(GMOCK_MATCHER_(tn, 1, __VA_ARGS__) gmock_a1) constness { \GMOCK_MOCKER_(1, constness, Method).RegisterOwner(this); \return GMOCK_MOCKER_(1, constness, Method).With(gmock_a1); \} \// The matcher type for argument N of the given function type.// INTERNAL IMPLEMENTATION - DON'T USE IN USER CODE!!!#define GMOCK_MATCHER_(tn, N, ...) \const ::testing::Matcher<GMOCK_ARG_(tn, N, __VA_ARGS__)>&

函数的参数类型都是Matcher模板类,而AnythingMatcher定义了Matcher<T>()方法用于返回一个Matcher<T>对象。

参数过滤

参数过滤是Gmock非常有用的一个功能,它让我们可以通过参数定义不同的调用场景。

Gmock中提供了两处设置参数过滤的地方,举个例子

EXPECT_CALL(test_user, Pay(Eq(1))).With(_).WillRepeatedly(testing::Return(true));

Pay中指定参数不能等于1,With则表示对参数没有限制。这就是两处参数约束。一般来说gmock##Method中的参数约束是针对各自参数的,而With则是关注于参数之间的关系。我们看下这两处约束是怎么工作的。
        以一个参数的版本为例,MOCK_METHOD1宏中

  ::testing::MockSpec<__VA_ARGS__>& \gmock_##Method(GMOCK_MATCHER_(tn, 1, __VA_ARGS__) gmock_a1) constness { \GMOCK_MOCKER_(1, constness, Method).RegisterOwner(this); \return GMOCK_MOCKER_(1, constness, Method).With(gmock_a1); \} \mutable ::testing::FunctionMocker<__VA_ARGS__> GMOCK_MOCKER_(1, constness, \Method)

gmock##Method方法调用了FunctionMocker模板类的With方法,该方法返回了一个MockSpec模板对象。gmock##Method方法是在EXPECT_CALL宏中被调用的。
        FunctionMocker中的With是这么实现的

  MockSpec<F>& With(const Matcher<A1>& m1) {this->current_spec().SetMatchers(::testing::make_tuple(m1));return this->current_spec();}

current_spec()是其基类FunctionMockerBase的方法,它返回了以FunctionMocker<R(A1)> 为模板的MockSpec对象。SetMatchers方法将参数的匹配规则设置到其底层的matchers_中

  void SetMatchers(const ArgumentMatcherTuple& matchers) {matchers_ = matchers;}

matchers_这个匹配规则会在调用EXPECT_CALL时通过下列两个方法保存起来

  // Adds a new default action spec to the function mocker and returns// the newly created spec.internal::OnCallSpec<F>& InternalDefaultActionSetAt(const char* file, int line, const char* obj, const char* call) {LogWithLocation(internal::kInfo, file, line,string("ON_CALL(") + obj + ", " + call + ") invoked");return function_mocker_->AddNewOnCallSpec(file, line, matchers_);}// Adds a new expectation spec to the function mocker and returns// the newly created spec.internal::TypedExpectation<F>& InternalExpectedAt(const char* file, int line, const char* obj, const char* call) {const string source_text(string("EXPECT_CALL(") + obj + ", " + call + ")");LogWithLocation(internal::kInfo, file, line, source_text + " invoked");return function_mocker_->AddNewExpectation(file, line, source_text, matchers_);}

具体的保存逻辑是

  // Adds and returns a default action spec for this mock function.OnCallSpec<F>& AddNewOnCallSpec(const char* file, int line,const ArgumentMatcherTuple& m)GTEST_LOCK_EXCLUDED_(g_gmock_mutex) {Mock::RegisterUseByOnCallOrExpectCall(MockObject(), file, line);OnCallSpec<F>* const on_call_spec = new OnCallSpec<F>(file, line, m);untyped_on_call_specs_.push_back(on_call_spec);return *on_call_spec;}// Adds and returns an expectation spec for this mock function.TypedExpectation<F>& AddNewExpectation(const char* file,int line,const string& source_text,const ArgumentMatcherTuple& m)GTEST_LOCK_EXCLUDED_(g_gmock_mutex) {Mock::RegisterUseByOnCallOrExpectCall(MockObject(), file, line);TypedExpectation<F>* const expectation =new TypedExpectation<F>(this, file, line, source_text, m);const linked_ptr<ExpectationBase> untyped_expectation(expectation);untyped_expectations_.push_back(untyped_expectation);// Adds this expectation into the implicit sequence if there is one.Sequence* const implicit_sequence = g_gmock_implicit_sequence.get();if (implicit_sequence != NULL) {implicit_sequence->AddExpectation(Expectation(untyped_expectation));}return *expectation;}

即以该规则为参数,新建了OnCallSpec<F>或TypedExpectation<F>对象,这两个对象将会被保存到各自的vector中。当mock的函数被调用时,Gmock将通过下面两个函数之一去检测参数是否匹配

  // Returns the ON_CALL spec that matches this mock function with the// given arguments; returns NULL if no matching ON_CALL is found.// L = *const OnCallSpec<F>* FindOnCallSpec(const ArgumentTuple& args) const {for (UntypedOnCallSpecs::const_reverse_iterator it= untyped_on_call_specs_.rbegin();it != untyped_on_call_specs_.rend(); ++it) {const OnCallSpec<F>* spec = static_cast<const OnCallSpec<F>*>(*it);if (spec->Matches(args))return spec;}return NULL;}
  // Returns the expectation that matches the arguments, or NULL if no// expectation matches them.TypedExpectation<F>* FindMatchingExpectationLocked(const ArgumentTuple& args) constGTEST_EXCLUSIVE_LOCK_REQUIRED_(g_gmock_mutex) {g_gmock_mutex.AssertHeld();for (typename UntypedExpectations::const_reverse_iterator it =untyped_expectations_.rbegin();it != untyped_expectations_.rend(); ++it) {TypedExpectation<F>* const exp =static_cast<TypedExpectation<F>*>(it->get());if (exp->ShouldHandleArguments(args)) {return exp;}}return NULL;}

这两个函数最终将在Matches函数中进行参数匹配

  // Returns true iff this expectation matches the given arguments.bool Matches(const ArgumentTuple& args) constGTEST_EXCLUSIVE_LOCK_REQUIRED_(g_gmock_mutex) {g_gmock_mutex.AssertHeld();return TupleMatches(matchers_, args) && extra_matcher_.Matches(args);}

这个函数中,还有extra_matcher_这种参数匹配规则。它是通过TypedExpectation模板类的With方法(不是FunctionMocker模板类的With方法)传递进来的

  // Implements the .With() clause.TypedExpectation& With(const Matcher<const ArgumentTuple&>& m) {if (last_clause_ == kWith) {ExpectSpecProperty(false,".With() cannot appear ""more than once in an EXPECT_CALL().");} else {ExpectSpecProperty(last_clause_ < kWith,".With() must be the first ""clause in an EXPECT_CALL().");}last_clause_ = kWith;extra_matcher_ = m;extra_matcher_specified_ = true;return *this;}

总结一下,Gmock的参数匹配通过FunctionMocker的With方法设置了一个通用匹配规则,还可以通过TypedExpectation的With方法设置额外的匹配规则,只有这两个匹配规则都满足时,才会被选中。

设定约束

我们主要分析下Times、WillOnce和WillRepeatedly这几个常见的约束。先回顾一个例子

EXPECT_CALL(test_user, Pay(_)).Times(5).WillOnce(testing::Return(true)).WillOnce(testing::Return(true)).WillRepeatedly(testing::Return(false));

这例子说,Pay行为有5次可控的执行次数,第6次执行就按默认值返回了。第1个WillOnce规定第一次执行Pay的行为,第2个WillOnce规定第二次执行Pay的行为,之后的3~5次都按WillRepeatedly规定的方式去执行。

我们先看Times的实现

  // Implements the .Times() clause.TypedExpectation& Times(const Cardinality& a_cardinality) {ExpectationBase::UntypedTimes(a_cardinality);return *this;}// Implements the .Times() clause.TypedExpectation& Times(int n) {return Times(Exactly(n));}
// Implements the .Times() clause.
void ExpectationBase::UntypedTimes(const Cardinality& a_cardinality) {if (last_clause_ == kTimes) {ExpectSpecProperty(false,".Times() cannot appear ""more than once in an EXPECT_CALL().");} else {ExpectSpecProperty(last_clause_ < kTimes,".Times() cannot appear after "".InSequence(), .WillOnce(), .WillRepeatedly(), ""or .RetiresOnSaturation().");}last_clause_ = kTimes;SpecifyCardinality(a_cardinality);
}// Explicitly specifies the cardinality of this expectation.  Used by
// the subclasses to implement the .Times() clause.
void ExpectationBase::SpecifyCardinality(const Cardinality& a_cardinality) {cardinality_specified_ = true;cardinality_ = a_cardinality;
}

执行次数最终被转换为Cardinality类的一个对象保存在FunctionMocker模板对象中。它将在IsSatisfied、IsSaturated和IsOverSaturated方法中被使用,用以判定执行的次数是否符合约定

  // Returns true iff this expectation is satisfied.bool IsSatisfied() constGTEST_EXCLUSIVE_LOCK_REQUIRED_(g_gmock_mutex) {g_gmock_mutex.AssertHeld();return cardinality().IsSatisfiedByCallCount(call_count_);}// Returns true iff this expectation is saturated.bool IsSaturated() constGTEST_EXCLUSIVE_LOCK_REQUIRED_(g_gmock_mutex) {g_gmock_mutex.AssertHeld();return cardinality().IsSaturatedByCallCount(call_count_);}// Returns true iff this expectation is over-saturated.bool IsOverSaturated() constGTEST_EXCLUSIVE_LOCK_REQUIRED_(g_gmock_mutex) {g_gmock_mutex.AssertHeld();return cardinality().IsOverSaturatedByCallCount(call_count_);}

参数中的call_count_就是函数执行的次数,它是在IncrementCallCount函数中实现自增。IncrementCallCount函数则是在获取行为时被调用到

  const Action<F>* GetActionForArguments(const FunctionMockerBase<F>* mocker,const ArgumentTuple& args,::std::ostream* what,::std::ostream* why)GTEST_EXCLUSIVE_LOCK_REQUIRED_(g_gmock_mutex) {g_gmock_mutex.AssertHeld();if (IsSaturated()) {
......IncrementCallCount();
......}IncrementCallCount();
......}

我们再看下WillOnce的实现

  // Implements the .WillOnce() clause.TypedExpectation& WillOnce(const Action<F>& action) {ExpectSpecProperty(last_clause_ <= kWillOnce,".WillOnce() cannot appear after "".WillRepeatedly() or .RetiresOnSaturation().");last_clause_ = kWillOnce;untyped_actions_.push_back(new Action<F>(action));if (!cardinality_specified()) {set_cardinality(Exactly(static_cast<int>(untyped_actions_.size())));}return *this;}

可见WillOnce将入参重新赋值给一个新建的Action<F>对象。然后将它保存到untyped_actions_列表中。最终它会在GetCurrentAction方法中,通过参数匹配后被取出

    return count <= action_count ?*static_cast<const Action<F>*>(untyped_actions_[count - 1]) :repeated_action();

上面代码中的repeated_action方法是在WillRepeatedly方法这被赋值的

  // Implements the .WillRepeatedly() clause.TypedExpectation& WillRepeatedly(const Action<F>& action) {if (last_clause_ == kWillRepeatedly) {ExpectSpecProperty(false,".WillRepeatedly() cannot appear ""more than once in an EXPECT_CALL().");} else {ExpectSpecProperty(last_clause_ < kWillRepeatedly,".WillRepeatedly() cannot appear ""after .RetiresOnSaturation().");}last_clause_ = kWillRepeatedly;repeated_action_specified_ = true;repeated_action_ = action;if (!cardinality_specified()) {set_cardinality(AtLeast(static_cast<int>(untyped_actions_.size())));}// Now that no more action clauses can be specified, we check// whether their count makes sense.CheckActionCountIfNotDone();return *this;}

再看下testing::Return(true)是怎么转换为Action<F>的。它的定义是

template <typename R>
internal::ReturnAction<R> Return(R value) {return internal::ReturnAction<R>(internal::move(value));
}

其中ReturnAction是个模板类,它重载了Action<F>()方法,将返回值转换为一个Action<F>对象

  template <typename F>operator Action<F>() const {typedef typename Function<F>::Result Result;GTEST_COMPILE_ASSERT_(!is_reference<Result>::value,use_ReturnRef_instead_of_Return_to_return_a_reference);return Action<F>(new Impl<R, F>(value_));}

在new一个Action<F>是,传入了一个Impl模板类对象,这个模板类有一个Perform方法,其实现就是返回期待的值

virtual Result Perform(const ArgumentTuple&) { return value_; }

那么Action<F>对象和这个Impl模板类是怎么联系的呢?我们看下Impl的定义

  template <typename R_, typename F>class Impl : public ActionInterface<F> {public:
……

而在Action模板类的内部有

template <typename F>
class Action {
……
Action(const Action& action) : impl_(action.impl_) {}
……Result Perform(const ArgumentTuple& args) const {
……    return impl_->Perform(args);}private:
……internal::linked_ptr<ActionInterface<F> > impl_;
};

很醒目,最终执行的行为将由Action类中的Impl_成员变量来执行,而该Impl_变量就是在Action被创建时传入的。

执行

当我们调用mock的类的mock的函数时,将会调用到MOCK_METHOD系列宏中定义的函数。以一个参数版本为例

  GMOCK_RESULT_(tn, __VA_ARGS__) ct Method( \GMOCK_ARG_(tn, 1, __VA_ARGS__) gmock_a1) constness { \GTEST_COMPILE_ASSERT_((::testing::tuple_size<                          \tn ::testing::internal::Function<__VA_ARGS__>::ArgumentTuple>::value \== 1), \this_method_does_not_take_1_argument); \GMOCK_MOCKER_(1, constness, Method).SetOwnerAndName(this, #Method); \return GMOCK_MOCKER_(1, constness, Method).Invoke(gmock_a1); \} \

其最终调用到FunctionMocker类的Invoke函数中

  R Invoke(A1 a1) {// Even though gcc and MSVC don't enforce it, 'this->' is required// by the C++ standard [14.6.4] here, as the base class type is// dependent on the template argument (and thus shouldn't be// looked into when resolving InvokeWith).return this->InvokeWith(ArgumentTuple(a1));}

调用InvokeWith之前,已将参数转换成一个ArgumentTuple对象,这样将方便之后统一处理。        InvokeWith函数内部使用了一个结果承载器——ResultHolder用于保存结果。InvokeWith最终会调用到FunctionMockerBase的PerformDefaultAction中

  Result PerformDefaultAction(const ArgumentTuple& args,const string& call_description) const {const OnCallSpec<F>* const spec =this->FindOnCallSpec(args);if (spec != NULL) {return spec->GetAction().Perform(args);}const string message = call_description +"\n    The mock function has no default action ""set, and its return type has no default value set.";
#if GTEST_HAS_EXCEPTIONSif (!DefaultValue<Result>::Exists()) {throw std::runtime_error(message);}
#elseAssert(DefaultValue<Result>::Exists(), "", -1, message);
#endifreturn DefaultValue<Result>::Get();}

第3行通过参数匹配相应的处理行为。找到行为后,在06行执行该行为;没有找到,则返回默认值。

至此,Gmock的主要流程相关的源码已经分析结束了。我们稍微总结下:

  • Mock的类通过MOCK_METHOD系列方法,声明了一个Mock函数的对象,并定义了一个通过该对象获取符合相应约束的函数对象。还定义了一个和需要mock的函数同名的函数,该函数内部完成最终的结果计算。
  • EXPECT_CALL宏和WillOnce、WillRepeatedly等方法,设定了函数对象的一些特性。
  • 最终用户调用函数时,将通过参数匹配得到适合的函数对象,并执行该函数对象中的预期行为。



Google Mock(Gmock)简单使用和源码分析——源码分析相关推荐

  1. Google Mock(Gmock)简单使用和源码分析——简单使用

    初识Gmock是之前分析GTest源码时,它的源码和GTest源码在同一个代码仓库中(https://github.com/google/googletest).本文我将以目前最新的Gmock1.7版 ...

  2. bytebuddy实现原理分析 源码分析 (三)- advice 详解

    advice详解 八.advice 8.1 AsmVisitorWrapper 8.1.1 ForDeclareFields 8.1.1.1 Entry 8.1.1.2 DispatchingVisi ...

  3. java 头尾 队列_源码|jdk源码之栈、队列及ArrayDeque分析

    栈.队列.双端队列都是非常经典的数据结构.和链表.数组不同,这三种数据结构的抽象层次更高.它只描述了数据结构有哪些行为,而并不关心数据结构内部用何种思路.方式去组织. 本篇博文重点关注这三种数据结构在 ...

  4. 【Android 进程保活】应用进程拉活 ( 系统 Service 机制拉活 | Service 组件 onStartCommand 方法分析 | 源码资源 )

    文章目录 一. Service 组件 onStartCommand 方法分析 1. onStartCommand 函数返回值分析 2. onStartCommand 函数 START_STICKY_C ...

  5. android 自定义相机源码,Android 自定义相机及分析源码

    Android 自定义相机及分析源码 使用Android 系统相机的方法: 要想让应用有相机的action,咱们就必须在清单文件中做一些声明,好让系统知道,如下 action的作用就是声明action ...

  6. easyloader [easyui_1.4.2] 分析源码,妙手偶得之

    用easyui很久了,但是很少去看源码. 有解决不了的问题就去百度... 今日发现,easyui的源码不难懂. 而且结合 easyloader 可以非常方便的逐个研究easyui的组件. 但是, ea ...

  7. 【Mybatis源码】源码分析

    [Mybatis源码]源码分析 (一)Mybatis重要组件 [1]四大核心组件 (1)SqlSessionFactoryBuilder (2)SqlSessionFactory (3)SqlSess ...

  8. bytebuddy实现原理分析 源码分析 (二)

    bytebuddy实现原理分析 &源码分析 四.字节码的操作(implementation ) pkg 4.1 bytecode :pkg 4.1.1 StackManipulation :c ...

  9. 【akka】akka源码 Akka源码分析-FSM

    1.概述 转载自己学习,建议直接看原文:Akka源码分析-FSM akka还有一个不常使用.但我觉得比较方便的一个模块,那就是FSM(有限状态机).我们知道了akka中Actor模型的具体实现之后,就 ...

最新文章

  1. 观点:AI 与自动化是矛盾的
  2. C++ 类 和 对象!
  3. HTML5 的优点与缺点
  4. [转]SupSite页面文件与模块关系说明
  5. optee3.14中的异常向量表解读--中断处理解读
  6. 人工智能——Singleton模式
  7. k8s部署nginx集群
  8. MyEclipse的Git配置
  9. java中的io复用_从 Java 中的零拷贝到五种IO模型
  10. iPhone XS Max第三方售价低于7千元 引用户疯抢
  11. CSS(网页样式语言)基础
  12. js获取服务器控件DropDownList所选中的各项属性
  13. R语言大作业(二):上海市人口普查数据分析
  14. 这可能不只是一篇面经
  15. 【动画消消乐|CSS】088.HTML+CSS实现自定义简易过渡动画
  16. Codesys学习调试笔记1
  17. 412 Precondition Failed
  18. 小沈阳最经典的99句台词 保证你从头笑到尾
  19. 深度学习之TensorFlow(一)
  20. word 目录格式调整

热门文章

  1. 如何判断基因组的重复区域_人类参考基因组GRCh37 VS GRCh38
  2. Python,OpenCV应用轮廓逼近算法,检测对象的形状
  3. python 点云las、laz文件的读取、写入、压缩
  4. 图像识别-opencv
  5. halcon与QT联合:(5.1)瓶盖检测以及QT界面搭建
  6. 上市公司财务报表分析——以中国联通为例
  7. archlinux升级内核后virtualbox不能启动虚拟机
  8. cstring 比较_不同商用齿轮齿接触分析软件的比较(三)
  9. DSP-SLAM:具有深度形状先验的面向对象SLAM
  10. ATS 5.3.0中自定义日志格式文件logs_xml.config解读