专栏首页方亮Google Mock(Gmock)简单使用和源码分析——源码分析

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

源码分析

        通过《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()> : public
    internal::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__>::Result
	
template <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)> : public
    internal::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) const
          GTEST_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) const
      GTEST_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() const
      GTEST_EXCLUSIVE_LOCK_REQUIRED_(g_gmock_mutex) {
    g_gmock_mutex.AssertHeld();
    return cardinality().IsSatisfiedByCallCount(call_count_);
  }

  // Returns true iff this expectation is saturated.
  bool IsSaturated() const
      GTEST_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() const
      GTEST_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_EXCEPTIONS
    if (!DefaultValue<Result>::Exists()) {
      throw std::runtime_error(message);
    }
#else
    Assert(DefaultValue<Result>::Exists(), "", -1, message);
#endif
    return DefaultValue<Result>::Get();
  }

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

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

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

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • DllMain中不当操作导致死锁问题的分析--导致DllMain中死锁的关键隐藏因子2

            本文介绍使用Windbg去验证《DllMain中不当操作导致死锁问题的分析--导致DllMain中死锁的关键隐藏因子》中的结论,调试对象是文中刚...

    方亮
  • VC开发Windows客户端软件之旅——前言

            从第一次拖着行李入京找活,至今已工作若干年了。这些年一直追逐自己的梦想,跑过三个城市,换了三份工作,认识了很多业内的朋友。和朋友们闲聊时,发现很...

    方亮
  • Colly源码解析——框架

            Colly是一个使用golang实现的数据抓取框架,我们可以使用它快速搭建类似网络爬虫这样的应用。本文我们将剖析其源码,以探析其中奥秘。(转载请...

    方亮
  • redux、mobx、concent特性大比拼, 看后生如何对局前辈

    redux、mobx本身是一个独立的状态管理框架,各自有自己的抽象api,以其他UI框架无关(react, vue...),本文主要说的和react搭配使用...

    腾讯新闻前端团队
  • 内核中根据进程Pid获取卷的全目录

    在内核中有时候想通过PID 获取进程的全路径以达到监控的作用 比如我们设置了进程回调.则可以根据PID看下进程的全路径.

    IBinary
  • React服务端渲染与同构实践

    前两年服务端渲染和同构的概念火遍了整个前端界,几乎所有关于前端的分享会议都有提到。在这年头,无论你选择什么技术栈,不会做个服务端渲染可能真的快混不下去了!最近刚...

    IMWeb前端团队
  • Effective STL(21) 永远让比较函数对相同元素返回false

    问题描述: 昨天一哥们些的程序,在定义比较函数的时候是这样写的 bool cmp(const T& a, const T& b) { if (a >=...

    程序员小王
  • 罗永浩双喜临门!锤子融资后京东又为坚果Pro举办百日宴

    近日锤子科技的融资引发行业不小关注, 10亿元新融资的助力也必将让老罗的业务越来越顺。不仅如此,日前京东也特意为坚果Pro举办百日宴,可谓双喜临门。看样子罗永浩...

    罗超频道
  • 【杂谈】想成为机器学习学霸?先学会做笔记吧

    话不多说,上标准。以下我总结了身边学编程的小哥哥们和小姐姐们对云笔记app的几类需求:

    用户1508658
  • 近几年前端技术盘点以及 2016 年技术发展方向

    Web 发展了几十个春秋,风起云涌,千变万化。我很庆幸自己没有完整地经历过这些年头,而是站在前人的肩膀上行走。Web 技术发展的速度让人感觉那几乎不是继承式的迭...

    用户1631416

扫码关注云+社区

领取腾讯云代金券