前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Google Mock(Gmock)简单使用和源码分析——源码分析

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

作者头像
方亮
发布2019-01-16 15:32:40
4.1K0
发布2019-01-16 15:32:40
举报
文章被收录于专栏:方亮方亮

源码分析

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

MOCK_METHOD系列宏

        首先我们以MOCK_METHOD0为例

代码语言:javascript
复制
#define MOCK_METHOD0(m, ...) GMOCK_METHOD0_(, , , m, __VA_ARGS__)

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

代码语言:javascript
复制
#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_的定义比较晦涩,通过这些醒目的定义,我们将会发现其各个参数的作用。

代码语言:javascript
复制
// 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_宏组装

代码语言:javascript
复制
#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属性、参数个数、方法名已经所在的行号组成。这样就尽可能的保证该变量在同一个文件中的唯一性。

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

代码语言:javascript
复制
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_的定义上

代码语言:javascript
复制
 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函数的返回类型

代码语言:javascript
复制
#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_中还定义了另一个方法

代码语言:javascript
复制
  ::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模板对象在之前我们见过,它是为了实现参数筛选而设计的。其具体实现我们在之后会分析。

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

代码语言:javascript
复制
// 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_宏指定参数类型

代码语言:javascript
复制
#define GMOCK_ARG_(tn, N, ...) \
    tn ::testing::internal::Function<__VA_ARGS__>::Argument##N

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

代码语言:javascript
复制
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模板类的两个参数的版本继承于一个参数的版本,一个参数版本继承于无参数版本。这样两个参数版本中,它从无参数版本中继承到了

代码语言:javascript
复制
typedef R Result;

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

代码语言:javascript
复制
typedef A1 Argument1;

        而自身定义了

代码语言:javascript
复制
typedef A2 Argument2;

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

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

代码语言:javascript
复制
MOCK_METHOD2(Login, bool(const std::string&, const std::string&));

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

代码语言:javascript
复制
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宏中就是调用了它。

代码语言:javascript
复制
#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。以下面的调用为例

代码语言:javascript
复制
EXPECT_CALL(test_user, Pay(_)).WillRepeatedly(testing::Return(true));

        其最终是这样的调用

代码语言:javascript
复制
test_user.gmock_Pay(_).InternalExpectedAt(__FILE__, __LINE__, 'test_user', 'Pay').WillRepeatedly(testing::Return(true));

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

代码语言:javascript
复制
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中参数的定义

代码语言:javascript
复制
  ::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中提供了两处设置参数过滤的地方,举个例子

代码语言:javascript
复制
EXPECT_CALL(test_user, Pay(Eq(1))).With(_).WillRepeatedly(testing::Return(true));

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

代码语言:javascript
复制
  ::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是这么实现的

代码语言:javascript
复制
  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_中

代码语言:javascript
复制
  void SetMatchers(const ArgumentMatcherTuple& matchers) {
    matchers_ = matchers;
  }

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

代码语言:javascript
复制
  // 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_);
  }

        具体的保存逻辑是

代码语言:javascript
复制
  // 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将通过下面两个函数之一去检测参数是否匹配

代码语言:javascript
复制
  // 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;
  }
代码语言:javascript
复制
  // 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函数中进行参数匹配

代码语言:javascript
复制
  // 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方法)传递进来的

代码语言:javascript
复制
  // 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这几个常见的约束。先回顾一个例子

代码语言:javascript
复制
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的实现

代码语言:javascript
复制
  // 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));
  }
代码语言:javascript
复制
// 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方法中被使用,用以判定执行的次数是否符合约定

代码语言:javascript
复制
  // 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函数则是在获取行为时被调用到

代码语言:javascript
复制
  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的实现

代码语言:javascript
复制
  // 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方法中,通过参数匹配后被取出

代码语言:javascript
复制
    return count <= action_count ?
        *static_cast<const Action<F>*>(untyped_actions_[count - 1]) :
        repeated_action();

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

代码语言:javascript
复制
  // 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>的。它的定义是

代码语言:javascript
复制
template <typename R>
internal::ReturnAction<R> Return(R value) {
  return internal::ReturnAction<R>(internal::move(value));
}

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

代码语言:javascript
复制
  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方法,其实现就是返回期待的值

代码语言:javascript
复制
virtual Result Perform(const ArgumentTuple&) { return value_; }

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

代码语言:javascript
复制
  template <typename R_, typename F>
  class Impl : public ActionInterface<F> {
   public:
……

        而在Action模板类的内部有

代码语言:javascript
复制
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系列宏中定义的函数。以一个参数版本为例

代码语言:javascript
复制
  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函数中

代码语言:javascript
复制
  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中

代码语言:javascript
复制
  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等方法,设定了函数对象的一些特性。
  • 最终用户调用函数时,将通过参数匹配得到适合的函数对象,并执行该函数对象中的预期行为。
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2016年05月18日,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 源码分析
    • MOCK_METHOD系列宏
      • EXPECT_CALL、ON_CALL宏        
        • 参数过滤        
          • 设定约束
            • 执行
            领券
            问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档