C++开发测试工具gmock使用详解(进阶)——对抽象接口类进行gmock打桩并测试

本文主要是介绍C++开发测试工具gmock使用详解(进阶)——对抽象接口类进行gmock打桩并测试,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

目录

    • 一、Mock深入理解
    • 二、进阶Demo
      • 2.1 共同约定
      • 2.2 小A任务
      • 2.3 我的任务
      • 2.4 编写Mock类
      • 2.5 在Test中使用Mock
      • 2.6 main()

上一篇文章(C++开发测试工具gmock使用详解——对抽象接口类进行gmock打桩并测试)主要介绍了对抽象接口类进行gmock打桩的基本流程并利用简单Demo进行测试。本文将介绍gmock打桩的进阶案例,结合上文一起食用效果更佳哦~

一、Mock深入理解

当开发者在单元测试、模块接口测试时,当这个模块需要依赖另外一个/几个类,而这时这些个类还没有开发好(因为某些原因),这时我们就可以定义了Mock对象来模拟那些没开发好类的行为。
Mock框架可以帮助比较方便、轻松地实现这些假的依赖类。

接下来介绍一下gmock的进阶代码,加深大家对于gmock打桩的了解。

二、进阶Demo

进阶代码实现海龟turtle运动的一个程序。

2.1 共同约定

与其让我编写的应用程序直接与系统 API 通信,不如将 API 包装在一个接口中(interface),然后编写代码实现该接口:

//接口
class Turtle {
public://控制turtle运动是否留下痕迹virtual void PenUp() = 0;	//画笔抬起virtual void PenDown() = 0;	//画笔落下//控制turtle运动方向virtual void Forward(int distance) = 0;	//前进virtual void Turn(int degrees) = 0;		//转向virtual void GoTo(int x, int y) = 0;	//前往(x,y)位置//获取turtle当前位置virtual int GetX() = 0;	//获取x的坐标virtual int GetY() = 0;	//获取y的坐标public:virtual ~Turtle() {}	//析构函数必须是virtual
};

在该接口中,使用PenUp()、PenDown()来控制turtle的画笔抬起与落下,使用Forward()、Turn()和GoTo()控制其运动,最后,使用GetX()和GetY()获取当前位置。

【注意】:析构函数必须是虚拟的virtual,因为打算从中继承所有类,否则当你通过基指针删除对象时,将不会调用派生类的析构函数,而且会获得损坏的程序状态,例如内存泄露。

2.2 小A任务

正常情况下,小A同学应该编写代码实现上述接口中的7个函数,但是由于种种原因(可能是小A同学比较懒或者是被隔离了没带电脑)导致部分代码没有编写完成,因此这时就需要Mock发挥作用。
小A同学编写的代码如下:

/*...*/

(实际上什么也没写)

2.3 我的任务

我的任务是根据小A同学编写完成的代码进行实现更高阶的相关功能。比如利用小A同学的上述相关接口检查是否可以绘制圆形(三点绘制一个圆),以及计算当前点x+y的数值,代码如下:

class Painter
{Turtle* turtle;
public:Painter(Turtle* turtle): turtle(turtle) {}bool DrawCircle(int, int, int) {turtle->PenDown();return true;}int DrawXandY() {int x = turtle->GetX();int y = turtle->GetY();return x + y;}};

2.4 编写Mock类

正常情况下,我的程序通常会使用小A同学编写完成的代码进行实现。但是在测试中,可以使用Mock进行代替。这样就可以轻松检查程序正在调用的程序、参数、顺序等。Mock代码如下:

class MockTurtle : public Turtle {
public:MOCK_METHOD0(PenUp,void());MOCK_METHOD0(PenDown,void());MOCK_METHOD1(Forward,void(int distance));MOCK_METHOD1(Turn,void(int degrees));MOCK_METHOD2(GoTo,void(int x, int y));MOCK_METHOD0(GetX,int());MOCK_METHOD0(GetY,int());
};

实现步骤:

  • 从Turtle类中派生出一个类MockTurtle;
  • 在子类的部分中,写public : MOCK_METHOD();
  • 接下来获取函数签名,将其剪切并粘贴到宏中,并添加一个逗号 ,在函数名称和返回类型之间;
  • 如果要Mock const 方法,请添加包含的第 4 个参数(括号是必需的)。(const)
  • 重复此步骤,直到要Mock的所有private函数都已完成。(不言而喻,抽象类中的所有纯虚拟方法都必须被Mock或override

使用gmock中的一个宏(macro)MOCK_METHOD来定义Turtle中的函数。
MOCK_METHOD定义如下:
MOCK_METHOD#1(#2, #3(#4) )

  • #1:要mock的方法共有几个参数;
  • #2:要mock的方法名称;
  • #3:这个方法的返回值类型;
  • #4:这个方法的具体参数

定义Mock类时,需要决定将其放在何处,有些人将其放在cpp源文件中,当Mock界面由同一个人编写时,这可以,但是当所有者更改它时,测试可能会中断。因此建议单独放在一个h文件中。

2.5 在Test中使用Mock

在Test中使用Mock类的基本流程是:

  1. 从命名空间testing中导入gMock名称,以便可以在非限定状态下使用它们(每个文件只需执行一次)。
  2. 创建Mock对象。
  3. 指定对它们的期望(一个方法将被调用多少次?用什么参数?它应该怎么做?等)。
  4. 练习一些使用Mock的代码; (可选)使用谷歌测试断言检查结果。如果模拟方法的调用次数超出预期或参数错误,将立即收到错误。
  5. 当Mock被破坏时,gMock将自动检查是否满足了对它的所有期望。
using ::testing::AtLeast;	//#1
using ::testing::Return;TEST(PainterTest, CanDrawSomething) {MockTurtle turtle;               // #2EXPECT_CALL(turtle, PenDown())   // #3.Times(AtLeast(1));Painter painter(&turtle);EXPECT_TRUE(painter.DrawCircle(0, 0, 10));      // #4EXPECT_CALL(turtle, GetX())		// #3.WillOnce(Return(10));		//x返回10EXPECT_CALL(turtle, GetY())		// #3.WillOnce(Return(20));		//y返回20EXPECT_EQ(30, painter.DrawXandY());	//#4
}

EXPECT_CALL(turtle, PenDown()).Times(AtLeast(1));
表示此测试至少会检查一次调用,如果对象未调用此方法,则测试将失败。

2.6 main()

main函数中代码如下:

int main(int argc, char **argv)
{testing::InitGoogleTest(&argc, argv);return RUN_ALL_TESTS();
}

运行结果:
在这里插入图片描述

完整代码可以参考:https://download.csdn.net/download/didi_ya/83470301


ok,以上便是本文的全部内容了,如果对你有所帮助,记得点个赞哟~

参考:

  1. https://www.cnblogs.com/pugang/p/9500352.html
  2. https://www.cnblogs.com/welkinwalker/archive/2011/11/29/2267225.html

这篇关于C++开发测试工具gmock使用详解(进阶)——对抽象接口类进行gmock打桩并测试的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/293283

相关文章

Java中流式并行操作parallelStream的原理和使用方法

《Java中流式并行操作parallelStream的原理和使用方法》本文详细介绍了Java中的并行流(parallelStream)的原理、正确使用方法以及在实际业务中的应用案例,并指出在使用并行流... 目录Java中流式并行操作parallelStream0. 问题的产生1. 什么是parallelS

MySQL数据库双机热备的配置方法详解

《MySQL数据库双机热备的配置方法详解》在企业级应用中,数据库的高可用性和数据的安全性是至关重要的,MySQL作为最流行的开源关系型数据库管理系统之一,提供了多种方式来实现高可用性,其中双机热备(M... 目录1. 环境准备1.1 安装mysql1.2 配置MySQL1.2.1 主服务器配置1.2.2 从

C++中unordered_set哈希集合的实现

《C++中unordered_set哈希集合的实现》std::unordered_set是C++标准库中的无序关联容器,基于哈希表实现,具有元素唯一性和无序性特点,本文就来详细的介绍一下unorder... 目录一、概述二、头文件与命名空间三、常用方法与示例1. 构造与析构2. 迭代器与遍历3. 容量相关4

Linux join命令的使用及说明

《Linuxjoin命令的使用及说明》`join`命令用于在Linux中按字段将两个文件进行连接,类似于SQL的JOIN,它需要两个文件按用于匹配的字段排序,并且第一个文件的换行符必须是LF,`jo... 目录一. 基本语法二. 数据准备三. 指定文件的连接key四.-a输出指定文件的所有行五.-o指定输出

Linux jq命令的使用解读

《Linuxjq命令的使用解读》jq是一个强大的命令行工具,用于处理JSON数据,它可以用来查看、过滤、修改、格式化JSON数据,通过使用各种选项和过滤器,可以实现复杂的JSON处理任务... 目录一. 简介二. 选项2.1.2.2-c2.3-r2.4-R三. 字段提取3.1 普通字段3.2 数组字段四.

C++中悬垂引用(Dangling Reference) 的实现

《C++中悬垂引用(DanglingReference)的实现》C++中的悬垂引用指引用绑定的对象被销毁后引用仍存在的情况,会导致访问无效内存,下面就来详细的介绍一下产生的原因以及如何避免,感兴趣... 目录悬垂引用的产生原因1. 引用绑定到局部变量,变量超出作用域后销毁2. 引用绑定到动态分配的对象,对象

Linux kill正在执行的后台任务 kill进程组使用详解

《Linuxkill正在执行的后台任务kill进程组使用详解》文章介绍了两个脚本的功能和区别,以及执行这些脚本时遇到的进程管理问题,通过查看进程树、使用`kill`命令和`lsof`命令,分析了子... 目录零. 用到的命令一. 待执行的脚本二. 执行含子进程的脚本,并kill2.1 进程查看2.2 遇到的

MyBatis常用XML语法详解

《MyBatis常用XML语法详解》文章介绍了MyBatis常用XML语法,包括结果映射、查询语句、插入语句、更新语句、删除语句、动态SQL标签以及ehcache.xml文件的使用,感兴趣的朋友跟随小... 目录1、定义结果映射2、查询语句3、插入语句4、更新语句5、删除语句6、动态 SQL 标签7、ehc

详解SpringBoot+Ehcache使用示例

《详解SpringBoot+Ehcache使用示例》本文介绍了SpringBoot中配置Ehcache、自定义get/set方式,并实际使用缓存的过程,文中通过示例代码介绍的非常详细,对大家的学习或者... 目录摘要概念内存与磁盘持久化存储:配置灵活性:编码示例引入依赖:配置ehcache.XML文件:配置

Java 虚拟线程的创建与使用深度解析

《Java虚拟线程的创建与使用深度解析》虚拟线程是Java19中以预览特性形式引入,Java21起正式发布的轻量级线程,本文给大家介绍Java虚拟线程的创建与使用,感兴趣的朋友一起看看吧... 目录一、虚拟线程简介1.1 什么是虚拟线程?1.2 为什么需要虚拟线程?二、虚拟线程与平台线程对比代码对比示例:三