• 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的代码,需要时刻牢记“依赖注入”这个原则。
    欢迎讨论。

  • 相关阅读:
    json对象与字符串的相互转换,数组和字符串的转换
    angularjs ng-csv 异步下载
    angular2 localStorage的使用
    ng-csv 异步数据下载
    微信小程序AES加密解密
    微信小程序Md5加密(utf-8汉字无影响)
    angular-file-upload封装为指令+图片尺寸限制
    angular+require前端项目架构搭建
    Inspinia_admin-V2.3原版(英文)
    hplus--H+ V2.3 (中文版)
  • 原文地址:https://www.cnblogs.com/envoy/p/4331016.html
Copyright © 2020-2023  润新知