生产代码中有很多类方法是非虚的,而为了在Gtest中解除这些非必需的依赖,可以通过Gmock的mock non-virtual methods using templates方法来达到目的。
在此之前,需要了解一种设计模式:Dependency Injection,依赖注入。虽然这个概念始于Java和.net,但在面向对象编程中,C++代码同样应该遵循。

Ps:软件工程中的一个重要的理念就是关注分离(Separation of concern, SoC)。依赖注入不是目的,它是一系列工具和手段,最终的目的是帮助我们开发出松散耦合(loose coupled)、可维护、可测试的代码和程序。这条原则的做法是大家熟知的面向接口,或者说是面向抽象编程。

如何重构代码达到DI的目的呢,下面是一个例子。
原代码:

class A{
public:
  int Funtion1(B& obj) {
    //do something
    std::string str = “mock non-virtual methods using templates”;
     auto rst = obj.Function2(str);
    //do something
  }
}
class B{
public:
int Funtion2(std::string _str){ puts(_str.c_str()); }
}

当我们对类A的方法Function1进行UT防护的时候,不关心其中类B的方法Function2的执行结果,这时候该如何对其进行mock呢(Function2是非虚的)?

在以上这种代码结构中,答案是无法进行mock!除非把Function2修改为virtual或者使用下面的方法:
修改后:

emplate <class T1 >
class RefactorA{
public:
  int Funtion1(T1 & obj) {
    //do something
    std::string str = “mock non-virtual methods using templates”;
    auto rst = obj.Function2(str);
    //do something
  }
}

重构之后,类RefactorA变成了类模板,在实例化的时候把依赖的类B显式的“注入”进去,这时候进行UT的时候,就可以把“注入”的类B的方法Function2 进行mock,代码如下:
//对类B中的Function2进行mock

class  mockB
{
public:
  MOCK_METHOD1(Funtion2, int (std::string ));
};

/对类A进行UT测试

class RefactorA _UT : public :: testing::Test
{
protected:
  virtual void SetUp(){}
  virtual void TearDown(){}   RefactorA < mockB > mockObjA;//实例化模板类
}; TEST_F(RefactorA _UT , Funtion1)
{
  //期望类B的方法Function2被调用至少一次,返回值为100,参数为任意字符串
  mockB mockObjB;
  EXPECT_CALL(mockObjB, Funtion2 (_))
  .Times(AtLeast())
  .WillOnce(Return());   auto rst = mockObjA.Function1( mockObjB );//注意这里传入的是mock出来的对象   EXPECT_TRUE( rst );
}

把类B的方法Function2 mock之后,UT的重点就可以放在对Function1的其它分支上了。

重点:将类A改写为类模板之后,在生产代码中,需要使用真正的类B对象来进行模板类的实例化,而在测试代码中,则需要使用mock出来的类B对象进行模板类的实例化。它们之间的无关的,这与mock接口类的虚函数有着本质的区别。

附:

类模板方法的声明和定义有以下4种方法,可以酌情使用:
① 荐做法是方法在定义的时候就进行实现(类RefactorA);
②  再者是,声明在模板类中,实现在模板类外,但要在一个文件中;
③  把方法的实现写入xxx.inl文件,然后在模板类的结尾使用#include “xxx.inl”;
④  把方法的实现写入xxx.cpp文件,但在cpp文件的最开始需要将模板类实例化。

总之,为了写出可UT的代码,需要时刻牢记“依赖注入”这个原则。
欢迎讨论。

mock non-virtual methods的更多相关文章

  1. What’s wrong with virtual methods called through an interface

    May 31, 2016 Calling a virtual method through an interface always was a lot slower than calling a st ...

  2. why do we need virtual methods in C++?

    http://stackoverflow.com/questions/2391679/why-do-we-need-virtual-methods-in-c Basic idea: when mark ...

  3. 【转载】#349 - The Difference Between Virtual and Non-Virtual Methods

    In C#, virtual methods support polymorphism, by using a combination of the virtual and override keyw ...

  4. Rhino Mock

    mock interfaces, delegates and classes, including those with parameterized constructors. set expecta ...

  5. [转载] google mock cookbook

    原文: https://code.google.com/p/googlemock/wiki/CookBook Creating Mock Classes Mocking Private or Prot ...

  6. CLR via C# 3rd - 08 - Methods

       Kinds of methods        Constructors      Type constructors      Overload operators      Type con ...

  7. 8.Methods(一)

    1.Instance Constructors and Classes (Reference Types) Constructors methods : 1.allow an instance of ...

  8. (转) Virtual function

    原文地址:http://en.wikipedia.org/wiki/Virtual_function In object-oriented programming, a virtual functio ...

  9. Should I expose asynchronous wrappers for synchronous methods?

    Lately I've received several questions along the lines of the following, which I typically summarize ...

  10. why pure virtual function has definition 为什么可以在基类中实现纯虚函数

    看了会音频,无意搜到一个frameworks/base/include/utils/Flattenable.h : virtual ~Flattenable() = 0; 所以查了下“纯虚函数定义实现 ...

随机推荐

  1. JsonPluginsUtil

    package utils; import java.lang.reflect.Field;import java.text.SimpleDateFormat;import java.util.Arr ...

  2. Linux 内核链表 list.h 的使用

    Linux 内核链表 list.h 的使用 C 语言本身并不自带集合(Collection)工具,当我们需要把结构体(struct)实例串联起来时,就需要在结构体内声明指向下一实例的指针,构成所谓的& ...

  3. python之组合与继承的使用场景

    1.什么时候使用类的组合?当类之间有显著的不同,并且较小的类是组成较大类所需要的组件,此时用类的组合较合理:场景:医院是由多个科室组成的,此时我们可以定义不同科室的类,这样医院的类我们可以直接使用各个 ...

  4. [Ynoi2019模拟赛]Yuno loves sqrt technology II

    题目大意: 给定一个长为\(n\)的序列,\(m\)次询问,每次查询一个区间的逆序对数. 32MB. 解题思路: 出题人题解 众所周知lxl是个毒瘤,Ynoi道道都是神仙题 二次离线莫队. 对于每个区 ...

  5. BZOJ 3028 食物 (生成函数+数学题)

    题面:BZOJ传送门 题目让我们求这些物品在合法范围内任意组合,一共组合出$n$个物品的方案数 考虑把每种食物都用生成函数表示出来,然后用多项式乘法把它们乘起来,第$n$项的系数就是方案数 汉堡:$1 ...

  6. 【Linux常见问题总结】

    1. 如何设置vim编辑器TAB的缩进量?自己在使用Linux编写Python脚本的时候发现TAB的缩进量总是太长,于是想自己修改下vim编辑器的缩进量. 在/etc/vim/ 文件夹下建立 .vim ...

  7. PuTTY_0.67.0.0工具链接linux

    1.虚拟机设置 在网络适配器中选中桥接模式,勾选复制物理网络链接状态(p)选项.点击确认. 2.开启虚拟机,检查是否安装有ssh服务器 a.查看是否启动ssh服务器 ps -a | grep ssh ...

  8. IP实时传输协议RTP/RTCP详解

    1.简介 目前,在IP网络中实现实时语音.视频通信和应用已经成为网络应用的一个主流技术和发展方向,本文详细介绍IP协议族中用于实时语音.视频数据传输的标准协议RTP( Real-time Transp ...

  9. ConcurrentHashMap 并发HashMap原理分析

        ConcurrentHashMap和Hashtable主要区别就是围绕着锁的粒度以及如何锁.如图   左边便是Hashtable的实现方式---锁整个hash表:而右边则是Concurrent ...

  10. easyui获取当前点击对象tabs的title和Index

    观察上面打开的tabs选项卡,肯定会有一个目前是被选中状态,而这个状态的class属性也肯定是和其他tabs不一样的,有个class等于tabs-selected的 var title = $('.t ...