zoukankan      html  css  js  c++  java
  • mock nonvirtual methods

    生产代码中有很多类方法是非虚的,而为了在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(1))
      .WillOnce(Return(100));
    
      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的代码,需要时刻牢记“依赖注入”这个原则。
    欢迎讨论。

  • 相关阅读:
    openldap
    Java实现 洛谷 P1200 [USACO1.1]你的飞碟在这儿Your Ride Is He…
    Java实现 洛谷 P1200 [USACO1.1]你的飞碟在这儿Your Ride Is He…
    Java实现 洛谷 P2141 珠心算测验
    Java实现 洛谷 P2141 珠心算测验
    Java实现 洛谷 P2141 珠心算测验
    Java实现 洛谷 P2141 珠心算测验
    Java实现 洛谷 P2141 珠心算测验
    Java实现 洛谷 P1567 统计天数
    Java实现 洛谷 P1567 统计天数
  • 原文地址:https://www.cnblogs.com/envoy/p/4331016.html
Copyright © 2011-2022 走看看