C++单元测试框架-gtest-5-gmock

2023-12-01 16:58

本文主要是介绍C++单元测试框架-gtest-5-gmock,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

什么是gmock?

Google Mock(简称 gmock) 是Google在2008年推出的一套针对C++的 Mock框架,它灵感取自于 jMockEasyMockharcreatGoogle mock是用来配合 google test 对C++项目做单元测试的。它依赖于 googletest

当你写一个原型或是测试的时候,直接去依赖真实的对象通常是不可行的或是不明智的。Mock对象实现与真实对象有着相同的接口, 但你可以去指定Mock对象在运行时它做什么( 比如,调用哪个函数,以什么顺序,调用多少次,使用什么参数,返回内容是什么,等等 )。

使用Google Mock有下面三个基本步骤:

  1. 使用简单的宏来描述你想Mock的接口,这些宏会自动扩展成你的Mock类的实现。
  2. 创建一些Mock对象,并用一种直观的语法去指定Mock对象的期望和行为。
  3. 用这些Mock对象测试代码。Google Mock会捕获那些违反期望的冲突。

示例代码

我们直接使用示例代码,来展示其使用方法:

#include <iostream>
#include <gtest/gtest.h>
#include <gmock/gmock.h>// Gmock最大只支持10个参数的函数mockclass IFileApi{
public:virtual int open(const char* path, int o_flag)=0;virtual int read(const char* path, char* buffer, int size, int &size_ret)=0;virtual int close(int fd)=0;
};class FileDataLoad {
public:FileDataLoad(IFileApi *api) : m_fileApi(api),m_szFileData(NULL),         m_nDataSize(0){ }~FileDataLoad() { if (m_szFileData != NULL) { delete [] m_szFileData; }}int LoadData(const char* path, int size){  int fd = m_fileApi->open(path, 0777);   // 打开文件if (fd <= 0) { return -1; } // 我们假设 fd > 0. (真实环境中可能=0) m_szFileData = new char[size+1];m_szFileData[size] = '\0';int size_ret = 0;// 读文件,仅仅作为示例,所以忽略参数sizeint ret = m_fileApi->read(path, m_szFileData, size, size_ret);if (ret != 0) {delete [] m_szFileData;return ret;}if (size != size_ret) { // 暂时不考虑           return -1;          }m_nDataSize = ret;// 关闭文件ret = m_fileApi->close(fd);     return ret;}char    *GetFileData() { return m_szFileData; }int     GetDataSize() { return m_nDataSize; }
private:IFileApi*   m_fileApi;char*       m_szFileData;int         m_nDataSize;
};class MockFileApi : public IFileApi{
public:MOCK_METHOD2(open, int(const char*, int));MOCK_METHOD4(read, int(const char*, char*, int, int &));MOCK_METHOD1(close, int(int));
};TEST(FileDataLoadTest, LoadData) 
{MockFileApi *api = new MockFileApi;FileDataLoad fileLoad((IFileApi*)api);int file_load_size = 100;// 我们期望当调用api->open()函数的时候,总是返回11,且这个函数仅被调用1次EXPECT_CALL(*api, open(::testing::_, ::testing::_)).Times(1)   // 期望被调用1次.WillRepeatedly(::testing::Return(11)); // 总是返回11// 我们期望调用 api->read 时, 设置第3个参数为 file_load_size 且返回 0。这个函数只被调用1次。 EXPECT_CALL(*api, read(::testing::_, ::testing::_, ::testing::_, ::testing::_)).Times(1).WillRepeatedly(::testing::DoAll(::testing::SetArgReferee<3>(file_load_size),::testing::Return(0)));// 我们期望调用 api->close 时, 返回0。 这个函数只被调用1次。EXPECT_CALL(*api, close(::testing::_)).Times(1).WillRepeatedly(::testing::Return(0));int ret = fileLoad.LoadData("/home/test_file", file_load_size); // 我们期望返回 0EXPECT_EQ(ret, 0);delete api;
}
int main(int argc, char **argv) {::testing::InitGoogleMock(&argc, argv);return RUN_ALL_TESTS();
}

--------------------------------<代码分析>----------------------------------------------

  1. 在这儿,我们只写了一个TEST,即只有一个测试用例(test case),测试的目标函数是 FileDataLoad:: LoadData()

  2. FileDataLoad::LoadData函数使用IFileApi*类型的成员变量m_fileApi 来完成加载文件内容的功能。首先打开文件(open),然后读取文件(read),最后关闭文件句柄(close)。使用IFileApi纯虚接口的目的在于, 可以扩展不同应用场景的文件内容加载。 比如可以实现一个本地文件内容加载的子类来实现文件内容加载: class LocalFileApi : public IFileApi 使用POSIX标准文件系统api来实现本地文件 openreadclose 。还可以实现一个云端文件内容加载:class CloudFileApi : public IFileApi,使用约定接口规范来实现云端文件 openreadclose

  3. 我们在测试FileDataLoad:: LoadData时,无需关心m_fileApi是本地的还是云端的还是其他什么的,我们只关心LoadData这个函数的逻辑是否正确,错误处理是否完整,是否有内存泄漏等等。

  4. 所以,我们只需模拟m_fileApi的open,read和close,并指定这三个函数一些操作(返回一些特定值, 设置一些特定返回参数等:在TEST宏后面大括号中,使用EXPECT_CALL来设定),以此来验证 FileDataLoad::LoadData的逻辑是否正确。

  5. 以模拟open函数为例:

    首先,定义Mock类,并继承自IFileApi纯虚基类,使用MOCK_METHOD宏声明open:

     MOCK_METHOD2(open, int(const char*, int)); 
    

    因为open有两个参数,所以使用MOCK_METHOD2。宏第一个参数为函数名,第二个参数为函数类型 然后,使用EXPECT_CALL来设置调用到这个函数时的期望,代码如下(gmock_test.cpp第86行):

     EXPECT_CALL(*api, open(::testing::, ::testing::)) .Times(1) .WillRepeatedly(::testing::Return(11));
    

    具体含义为,在这个期望设置完以后,只会发生一次api->open调用(.Times(1)的作用),并且api->open被调用时的返回值为11(.WillRepeatedly(::testing::Return(11)的作用)

另外”::testing::_”是一种参数匹配语法,它表示匹配任意类型的参数,这里这样写是我们这个测试用例不关心参数类型。


其它请参考:

  1. <C++单元测试框架-gtest-1-断言>
  2. <C++单元测试框架-gtest-2-事件机制>
  3. <C++单元测试框架-gtest-3-参数化>
  4. <C++单元测试框架-gtest-4-深入解析gtest

这篇关于C++单元测试框架-gtest-5-gmock的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



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

相关文章

从入门到精通C++11 <chrono> 库特性

《从入门到精通C++11<chrono>库特性》chrono库是C++11中一个非常强大和实用的库,它为时间处理提供了丰富的功能和类型安全的接口,通过本文的介绍,我们了解了chrono库的基本概念... 目录一、引言1.1 为什么需要<chrono>库1.2<chrono>库的基本概念二、时间段(Durat

C++20管道运算符的实现示例

《C++20管道运算符的实现示例》本文简要介绍C++20管道运算符的使用与实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧... 目录标准库的管道运算符使用自己实现类似的管道运算符我们不打算介绍太多,因为它实际属于c++20最为重要的

Visual Studio 2022 编译C++20代码的图文步骤

《VisualStudio2022编译C++20代码的图文步骤》在VisualStudio中启用C++20import功能,需设置语言标准为ISOC++20,开启扫描源查找模块依赖及实验性标... 默认创建Visual Studio桌面控制台项目代码包含C++20的import方法。右键项目的属性:

Spring 框架之Springfox使用详解

《Spring框架之Springfox使用详解》Springfox是Spring框架的API文档工具,集成Swagger规范,自动生成文档并支持多语言/版本,模块化设计便于扩展,但存在版本兼容性、性... 目录核心功能工作原理模块化设计使用示例注意事项优缺点优点缺点总结适用场景建议总结Springfox 是

c++中的set容器介绍及操作大全

《c++中的set容器介绍及操作大全》:本文主要介绍c++中的set容器介绍及操作大全,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧... 目录​​一、核心特性​​️ ​​二、基本操作​​​​1. 初始化与赋值​​​​2. 增删查操作​​​​3. 遍历方

解析C++11 static_assert及与Boost库的关联从入门到精通

《解析C++11static_assert及与Boost库的关联从入门到精通》static_assert是C++中强大的编译时验证工具,它能够在编译阶段拦截不符合预期的类型或值,增强代码的健壮性,通... 目录一、背景知识:传统断言方法的局限性1.1 assert宏1.2 #error指令1.3 第三方解决

C++11委托构造函数和继承构造函数的实现

《C++11委托构造函数和继承构造函数的实现》C++引入了委托构造函数和继承构造函数这两个重要的特性,本文主要介绍了C++11委托构造函数和继承构造函数的实现,具有一定的参考价值,感兴趣的可以了解一下... 目录引言一、委托构造函数1.1 委托构造函数的定义与作用1.2 委托构造函数的语法1.3 委托构造函

C++11作用域枚举(Scoped Enums)的实现示例

《C++11作用域枚举(ScopedEnums)的实现示例》枚举类型是一种非常实用的工具,C++11标准引入了作用域枚举,也称为强类型枚举,本文主要介绍了C++11作用域枚举(ScopedEnums... 目录一、引言二、传统枚举类型的局限性2.1 命名空间污染2.2 整型提升问题2.3 类型转换问题三、C

C++链表的虚拟头节点实现细节及注意事项

《C++链表的虚拟头节点实现细节及注意事项》虚拟头节点是链表操作中极为实用的设计技巧,它通过在链表真实头部前添加一个特殊节点,有效简化边界条件处理,:本文主要介绍C++链表的虚拟头节点实现细节及注... 目录C++链表虚拟头节点(Dummy Head)一、虚拟头节点的本质与核心作用1. 定义2. 核心价值二

C++ 检测文件大小和文件传输的方法示例详解

《C++检测文件大小和文件传输的方法示例详解》文章介绍了在C/C++中获取文件大小的三种方法,推荐使用stat()函数,并详细说明了如何设计一次性发送压缩包的结构体及传输流程,包含CRC校验和自动解... 目录检测文件的大小✅ 方法一:使用 stat() 函数(推荐)✅ 用法示例:✅ 方法二:使用 fsee