Google Mock

C++开发测试工具gmock使用详解(进阶)——对抽象接口类进行gmock打桩并测试 目录一、Mock深入理解二、进阶Demo2.1 共同约定2.2 小A任务2.3 我的任务 上一篇文章(C++开发测试工具gmock使用详解——对抽象接口类进行gmock打桩并测试)主要介绍了对抽象接口类进行gmock打桩的基本流程并利用简单Demo进行测试。本文将介绍gmock打桩的进阶案例,结合上文一起食用效果更佳哦~ 一、Mock深入理解 当开发者在单元测试、模块接口测试时,当这个模块需要依赖另外一个/几个类,而这时这些个类还没有开发好(因为某些原因),这时我们就可以定义了Mock对象来模拟那些没开发好 阅读详情

源码分析
        通过《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的类中定义两个方法和一个变量:

GMOCK_RESULT_(tn, __VA_ARGS__) ct Method(……)
::testing::MockSpec<__VA_ARGS__>& gmock_##Method(……)
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等方法,设定了函数对象的一些特性。
最终用户调用函数时,将通过参数匹配得到适合的函数对象,并执行该函数对象中的预期行为。
--------------------- 
作者:breaksoftware 
来源:优快云 
原文:https://blog.youkuaiyun.com/breaksoftware/article/details/51438033 
版权声明:本文为博主原创文章,转载请附上博文链接!

探索 Google Test: 从基础断言到高级 Mock 技巧 本文系统地介绍了 Google Test 框架的使用, 从基础断言方法到高级测试技巧, 包括异常测试, 退出测试和 Mock 行为模拟. 通过具体的代码示例, 展示了如何测试整型, 字符串, 浮点数等不同数据类型, 以及如何在复杂场景下模拟接口类行为. 阅读详情

相关推荐

Google Mock(GMock):C++单元测试的高效模拟框架详解

Google Mock(GMock)是C++单元测试中的核心工具,能够高效隔离外部依赖并验证复杂交互逻辑。本文详细介绍了GMock的核心功能、典型使用场景、高级用法及注意事项,帮助开发者掌握如何利用GMock构建灵活、可靠的单元测试框架。通过模拟对象创建、行为控制、调用验证等功能,GMock能够显著提升测试的精细度和场景覆盖能力,适用于复杂依赖链测试、异常边界测试及性能敏感测试等场景。

青蛙博客 2770

Gtest/Gmock探究(三)-- MOCK_METHODX系列宏分析

这篇文章紧接着前面两篇博客《Gtest/Gmock探究(一)– 经典示例代码》和《Gtest/Gmock探究(二)– TEST宏分析》,第一篇给出了初识gtest框架的经典示例代码,第二篇详细分析了TEST宏的机制原理。本篇将详细分析gmock的MOCK_METHOD系列宏。首先简单介绍一下MOCK_METHOD宏的使用方法,然后进行宏展开并静态分析展开后的代码,最后使用gdb调试跟进测试程序,

Mr.H的专栏 3万+

Google Test与Google Mock源码解析及实战应用

简介:Google Test(gtest)是Google推出的开源C++单元测试框架,配合Google Mock(gmock)可实现强大的测试驱动开发。本资源包含gtest-1.7.0和gmock-1.7.0源码,涵盖测试套件、断言机制、模拟对象、参数化测试等核心功能。通过该框架,开发者可编写结构清晰、可维护性强的测试用例,支持跨平台编译与运行,广泛应用于TDD流程中,有效提升代码质量与可靠性。

weixin_35750747的博客 929

C++类对象单元测试中的Mock使用

文章目录前言1. 下载网址2. 为什么选择Google Mock3. C++简单的例子3.1 使用流程4. 指定自己的期望 前言   在进行单元测试时,我们想要测试自己缩写函数A,但是函数A却依赖于函数B,当函数B无法满足预期时就无法对函数A进行测试,主要由于下面几个原因: 函数B依赖于硬件设备 真实的函数B的返回值无法满足我们的预期 团队开发中函数B尚未实现   这时就需要对函数B进行打桩(仿真mock),使其达到我们预期的效果。 但是如同下面这种类型函数时,则需要深入下去,对基层的函数进行打桩: t

lzg_na的博客 1万+

[c语言]用辗转相除法求最大公约数,递归和不递归两种

在c语言的学习中必然会遇到求两个数之间的最大公约数的题目,那么对于其他经典方法,最简便快捷的方式就是利用辗转相除法 辗转相除法又叫做欧几里得算法,目的是为了求两个正整数的最大公约数 定理:gcd(a,b) = gcd(b ,a%b) //在这里用(a,b)表示a和b的最大公约数证明:a可以表示成a = kb + r,则r = a %b 假设d是a,b的一个公约数,则有 d|a, d|b,而r = a - kb,因此d|r //这里的"|"的意思是 :d是b的因数 ,d是a的因数 因此d...

ZZRBlogger的博客 1万+

【转载】参数估计(Parameter Estimation):频率学派(最大似然估计MLE、最大后验估计MAP)与贝叶斯学派(贝叶斯估计BPE)

基础 频率学派与贝叶斯学派 http://www.douban.com/group/topic/16719644/ http://www.zhihu.com/question/20587681 最大似然估计(Maximum likelihood estimation,MLE) http://baike.baidu.com/view/1918804.htm http://baike.baidu.co

影 的专栏 3413

mock的使用

1、使用mock模拟接口返回数据 2、对mock进行简单的封装 mock_method 调用的方法名 request_data 请求值 url请求url method post/get response_data 返回值 from mock import mock # 模拟mock封装 def mock_test(mock_menthod,request_data,url,metho...

qq_42350970的博客 741

CST Studio Suite安装详细教程(附安装包)CST Studio Suite 2024超详细安装教程

CST Studio Suite 2024安装指南摘要:本文详细介绍了CST Studio Suite 2024电磁仿真软件的下载与安装流程,包含23个图文步骤说明,从解压安装包到配置许可证服务器。重点提示了以管理员身份运行、自定义安装路径等关键步骤,并提供了常见问题解决方案和入门技巧。该软件适用于天线设计、PCB分析等高频电磁场仿真,安装后需通过localhost:27000连接许可证服务器完成激活。

pythonqiang9的博客 1万+

google mock C++单元测试框架

Google Test 1. 自定义错误输出: ASSERT_EQ(x.size(), y.size()) "Vectors x and y are of unequal length"; for (int i = 0; i x.size(); ++i) {   EXPECT_EQ(x[i], y[i]) "Vectors x and y differ at i

lq87002128的专栏 3915

Calibre 3Dstack --每日一个命令day27【overlap】(3-27)

本文介绍了Calibre3Dstack中overlap命令的使用方法。该命令用于检查两个已放置层之间的重叠量是否符合要求,支持设置检查名称、层类型、堆叠范围、检查方向等参数。关键参数包括:-constraint指定重叠约束条件,-exact控制百分比精度,-by_area切换为面积检查模式,-intersection报告相交区域。检查结果可通过RVE选项定制显示方式,适用于3D堆叠设计中的层间重叠验证场景。

u013620135的博客 112

探索激光焊接光束质量诊断系统:原理、应用与前沿

激光焊接光束质量诊断系统是一套专门用于精确评估和监测激光焊接过程中光束特性的精密设备。它宛如一个精密的 “光束医生”,能够全方位地对激光光束进行 “体检”,为焊接工艺提供关键的数据支持。探测器:作为系统的 “感知触角”,探测器负责捕捉激光光束的各种信息,其性能直接影响着诊断的准确性。例如,常见的 CCD(电荷耦合器件)探测器和 CMOS(互补金属氧化物半导体)探测器,它们能够将光信号转化为电信号,具有高灵敏度、高分辨率等特点,可精确探测光束的强度分布、光斑位置等参数。光学透镜。

远程部署调试 运行安装 项目调试 二次开发 项目技术新 持续迭代 部分源码免费分享 2583

Google Mock 入门

Google Mock 入门 原文 概述 什么是Mock? Mock,更确切地说应该是Mock Object。它究竟是什么?它有什么作用?在这里,我也只能先说说我的理解。 比如当我们在单元测试、模块的接口测试时,当这个模块需要依赖另外一个/几个类,而这时这些个类还没有开发好(那名开发同学比较懒,呵呵),这时我们就可以定义了Mock对象来模拟那些类的行为。 说得更直白一些,就是自己实现一个假的依赖类,对这个类的方法你想要什么行为就可以有什么行为,你想让这个方法返回什么结果就可以返回怎么样的结果。 但这时很多同

weixin_43254989的博客 1877

手把手教你学Simulink——基于Simulink的无迹卡尔曼滤波(UKF)控制仿真建模示例

本文详细介绍了在Simulink中实现无迹卡尔曼滤波(UKF)控制倒立单摆系统的完整过程。通过对比扩展卡尔曼滤波(EKF),重点阐述了UKF在处理强非线性系统时的优势:采用无迹变换(UT)通过确定性采样捕捉非线性特性,精度可达三阶泰勒展开且无需计算雅可比矩阵。文章分步骤讲解了状态空间定义、UKF参数设计、Simulink实现方法,以及结合LQR控制器的闭环系统搭建,并通过仿真验证UKF在大角度偏移情况下具有更优的估计精度和控制性能。最后还讨论了工程实践要点和扩展方向,为非线性系统控制提供了实用解决方案。

MHD0815的博客 96

【Google mock(g_mock)学习记录】

Google mock 学习记录

weixin_43966547的博客 2304

SAP Integration Suite 与 SAP Cloud Integration 的关系解析

SAP Cloud Integration是SAP Integration Suite套件的核心组件,专注于消息与流程集成。两者关系可概括为:Integration Suite是SAP BTP上的集成平台套件(iPaaS),而Cloud Integration是其中负责应用与B2B集成的关键能力。日常所说的"CPI"实际指代的是Integration Suite中的Cloud Integration功能。该套件还包含API管理、事件网格等其他集成能力,共同构成企业集成解决方案。理解这一层

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 594

【一 扣子客户案例合集】【 1.1 抖音电商:通过扣子智能体重塑电商服务新体验】

**摘要:**抖音电商借助扣子(Coze)智能体平台重塑客服体验,通过无代码开发、多模态交互和跨平台发布等功能,解决了传统客服高峰期压力大、成本高及响应机械等痛点。智能体采用直接面客与人工监管结合模式,实现高效精准服务,并通过解构流程、模块化设计优化系统架构。其技术优势包括零配置学习、意图识别、情绪感知等,实际应用中显著提升客服效率300%以上,改善用户体验。未来将进一步拓展至商品推荐、直播互动等场景,推动电商服务智能化升级。

商务合作|问题讨论|交流学习 请联系作者微信,加微信请务必注明来意,博客主页有联系方式 1569

Google Mock进阶篇 (Google Mock Cookbook译文)

原文链接:http://quweiprotoss.blog.163.com/blog/static/40882883201222721548449/ Google C++ Mocking Cookbook Version: 0.32 作者:Adrian Alexander 译者:Koala++ /屈伟 最新pdf版下载    你来对地方了,这里你可以找到Go

幽雨雨幽 1万+

廖雪峰Python教程[完整版]

廖雪峰的python教程整理的,已经排好版了,而且还是完整的,适合小白

【gmock】Google Mock 入门

目录1 概述1.1 什么是Mock?1.2 Google Mock概述1.3 参考文档2 Google Mock使用2.1 最简单的例子2.2 典型的流程2.3 自定义方法/成员函数的期望行为2.3.1 Matcher(匹配器)2.3.2 基数(Cardinalities)2.3.3 行为(Actions)2.3.4 序列(Sequences)3 Mock实践3.1 我改过的例子3.2 现实中的例子4 Google Mock Cookbook4.1 Mock protected、private方法4.2 M

梦心之魂的博客 5012

Outlook Express 6 (OE6) for Win7/Win8/Win10

英文版 Outlook Express6.0, 兼容Win7/Win10/WinXP,解压就可用。

SIMATIC S7-1200, S7-1500 PID 控制[手册]

SIMATIC S7-1200, S7-1500 PID 控制[手册]

Google/Mock

本文引用自http://www.tuicool.com/articles/n26rMvy 对于 c++ 来说写单元测试和 mock 框架不是一件容易的事情。还好, Google 为我们搭建了一个出色的单元测试和 mock 框架。网上的例子很多都过多强调概念,本文用一个简单的例子让大家对于什么是 gtest 和 gmock 让大家有一个直观的了解,让大家很快上手,就像写 hello word

mqdChalali的博客 709

efm32 入门.pdf

本PDF共计22页,介绍的是操作驱动API,下载与否自己斟酌。

Google Test 与 Google Mock:C++ 测试与模拟的完美结合

本文深入解析 Google Test(GTest)和 Google Mock(GMock)的核心功能与使用方法,探讨两者在 C++ 项目中的联合应用及集成策略。通过详细的功能介绍、代码示例以及实践注意事项,帮助开发者高效利用这两款工具提升代码质量与可维护性。

青蛙博客 1856

Google Mock 集成指南:WeexCore 测试体系中的 C++ Mock 框架详解

Google Mock 是 Google 出品的 C++ 模拟(Mock)框架,用于编写和使用 C++ mock 类,帮助开发者改进系统设计并编写更高质量的测试。在 Weex 开源仓库中,Google Mock 与 Google Test 一起以第三方源码形式随 WeexCore 原生内核(`weex_core/`)的测试工程引入,为 `weex_core` 的 C++ 层(如 wson、布局引擎

gitblog_00015的博客 415

Google Mock CheatSheet 全解:WeexCore 测试工程中的 Mock 速查手册

本文以仓库中内嵌的 Google Mock 官方速查手册(CheatSheet)为骨架,系统梳理 Google Mock 的全部核心 API:从 Mock 类的定义、测试中的典型使用流程,到 Matchers、Actions、Cardinalities、期望顺序控制与验证重置的完整速查表格。同时结合本仓库 `weex_core/test` 测试工程的实际集成方式,说明这套测试框架如何被引入、构建与

gitblog_00735的博客 830

google mock《转载》

http://blog.chinaunix.net/uid-25711527-id-3225605.html 分类:  原文地址:转:google mock C++单元测试框架 作者:奋飞的菜鸟 Content Matcher(匹配器) 基数(Cardinalities) 行为(Actions) 序列(Seq

liuhex的专栏 858

Google Mock启蒙篇matcher详细尽说明

Google Mock启蒙篇 [2] (Google C++ Mocking Framework for Dummies 翻译) 来自:Koala++'s blog   |  时间:2012-08-06 19:24:04   原文链接: http://quweiprotoss.blog.163.com/blog/static/4088288320111022103458507/

linuxheik的专栏 4935

google mock分享

转一篇小亮同学的google mock分享 Content Matcher(匹配器) 基数(Cardinalities) 行为(Actions) 序列(Sequences) Google Mock 入门 概述 Google Mock使用 Mock实践 Google Mock Cookbook 什么是Mock? Google Moc

jfkidear的专栏 1万+
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值