Loading [MathJax]/jax/output/CommonHTML/config.js
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >C++雾中风景番外篇2:Gtest 与 Gmock,聊聊C++的单元测试

C++雾中风景番外篇2:Gtest 与 Gmock,聊聊C++的单元测试

原创
作者头像
HappenLee
发布于 2018-11-01 10:35:44
发布于 2018-11-01 10:35:44
4.9K0
举报

正式工作之后,公司对于单元测试要求比较严格。(笔者之前比较懒,一般很少写完整的单测~~)。作为一个合格的开发工程师,需要为所编写代码编写适量的单元测试是十分必要的,在实际进行的开发工作之中,TDD(Test drivern development) 是一种经过实践可行的开发方式。编写单元测试可以帮助我们在开发阶段就发现错误,并且保证新的修改没有破坏已有的程序逻辑。 在 C++之中,常用的测试框架有 Gtest,Boost test,CPPUint 等。正是由于 Gmock 的加持,让 Gtest 在多种测试框架之中脱颖而出。今天笔者在这里要和大家聊聊的就是目前我司主力在使用的Gtest,以及配套的 Gmock,通过两者的配合使用,相信能够搞定绝大多数的测试场景了。

1.Gtest 的安装

笔者目前使用的系统是Deepin 15.6,是基于 Debian jessie的一款国内发行版。安装 Gtest 和 GMock 十分简单:

代码语言:txt
AI代码解释
复制
sudo apt-get install libgtest-dev libgmock-dev 

其他发行版如:Ubuntu,Centos 应该也可以通过自带的包管理软件就可以完成安装了。但是如果包管理软件之中没有带上对应的开发包,也可以选择编译安装:

  • 下载源码 git clone https://github.com/google/googletestcd build && cmake .. && make -j 2sudo make install之后只要在/usr/include路径下找到gtest.h,gmock.h就说明我们安装成功了。之后只需要在 CMake 之中链接对应的静态库,就可以利用 Gtest 进行单元测试了。
  • 用 CMake 生成 Makefile之后直接 make 编译
  • 最后进行安装
2.Gtest 的使用

Gtest 十分容易上手,通过其中的定义的宏就可以轻松实现要进行单元测试。并且其中每个单元测试都会计算出对应执行时间,可以通过执行时间来分析代码的执行效率。

测试函数TEST

先举个简单的栗子,假如现在我们需要测试一下函数来判断质数,代码如下:

代码语言:txt
AI代码解释
复制
bool is_prime(int num) {
    if (num < 2)
        return false;
    for(int i = 2; i <= sqrt(num) + 1; i++) {
        if (num % i == 0)
            return false;
    }
    return true;
}

现在我们用 Gtest 对这个函数进行测试,TEST的宏定义代表了会被RUN_ALL_TESTS执行的测试函数。在 Gtest 之中提供了两类断言ASSERT_*系列和EXPECT_*系列。两者的区别就在于,ASSERT 失败之后就不会运行后续的测试了,但是 EXPECT 虽然失败,但是不影响后续测试的进行。看起来EXPECT会更加灵活一些,尤其是需要释放一些资源或执行一些其他逻辑时,更适合用EXPECT

代码语言:txt
AI代码解释
复制
TEST(test_prime, is_true) {
    EXPECT_TRUE(is_prime(5));
    ASSERT_TRUE(is_prime(5));
    EXPECT_TRUE(is_prime(3));
}

TEST(test_prime, is_false) {
    ASSERT_FALSE(is_prime(4));
    EXPECT_FALSE(is_prime(4));
}

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

testing::InitGoogleTest 初始化测试框架,必须在运行测试之前调用 RUN_ALL_TESTS 会运行所有由TEST 宏定义的测试。测试结果如下图所示:我们定义的is_true和 is_false同属同一个测试 case:test_prime,并且成功通过了测试。

测试结果
测试结果

上面我们使用了这TRUE 与 FALSE 的判断宏:

判断宏
判断宏

Gtest 提供了多种的判断宏,包括字符串的判断,数值判断等等,具体的细节可以参照 Gtest 的官方文档,笔者这里不再赘述。

测试函数TEST_F

很多时候,我们进行一些测试的时候需要进行资源初始化工作,进行资源复用,最后回收资源。这样的场景就适合使用 TEST_F的宏来进行测试。TEST_F适用于多种测试场景需要相同数据配置的情况,利用了 C++继承类来实现对父类方法的测试。举个例子,笔者实现了一个跳表,下面是对跳表进行测试的代码:

代码语言:txt
AI代码解释
复制
class Test_Skiplist : public testing::Test {
public:
    virtual void SetUp() {
        std::cout << "Set Up Test" << std::endl;
        _sl->load();

    }
    virtual void TearDown() {
        std::cout << "Tear Down Test" << std::endl;
        _sl->dump();
    }

    ~Test_Skiplist(){};

    SkipList* _sl = new SkipList();
};

TEST_F(Test_Skiplist, insert) {
    std::string test_string("happen");
    ASSERT_EQ(_sl->insert("1", test_string.c_str(), test_string.size()), Status::SUCCESS);
    test_string = "lee";
    ASSERT_EQ(_sl->insert("2", test_string.c_str(), test_string.size()), Status::SUCCESS);

    uint64_t data64 = 50;
    ASSERT_EQ(_sl->insert("50", reinterpret_cast<char *>(&data64), 8), Status::SUCCESS);
    uint32_t data32 = 20;
    ASSERT_EQ(_sl->insert("20", reinterpret_cast<char *>(&data32), 4), Status::SUCCESS);
}

TEST_F(Test_Skiplist, search) {
    ASSERT_EQ(_sl->search("1")->value_size, 6);
    ASSERT_STREQ(std::string(_sl->search("1")->value.get()).c_str(), "happen");

    ASSERT_EQ(_sl->search("3"), nullptr);
}

TEST_F(Test_Skiplist, remove) {
    ASSERT_EQ(_sl->remove("1"), Status::SUCCESS);
    ASSERT_EQ(_sl->remove("1"), Status::FAIL);

    ASSERT_EQ(_sl->search("1"), nullptr);
}

由上述代码可以看到,通过 TEST_F进行的测试类需要继承testing::Test类。同时要实现对应的 SetUpTearDown方法,SetUp方式执行资源的初始化操作,而TearDown则负责资源的释放。需要注意的是,上述代码我们测试了跳表的search,remove,insert方法,而每个测试是独立的进行的。也就是每个测试执行时都会运行对应的SetUp和 TearDown方法。

命令行参数

编译生成二进制的测试执行文件之后,直接运行就可以执行单元测试了。但是 Gtest 提供了一些命令行参数来帮助我们更好的使用,下面介绍一下笔者常用的几个命令行参数:

  • --gtest_list_tests 列出所有需要执行的测试,但是并不执行。
  • --gtest_filter 对所执行的测试进行过滤,支持通配符 ? 单个字符 * 任意字符 - 排除 ./test --gtest_filter=SkTest.*-SkTest.insert 表示运行所有名为SkTest的案例,排除了SkTest.insert这个案例。
  • --gtest_repeat=count 设置测试重复运行的次数,其中-1表示无限执行。
3.Gmock 的使用

上述 Gtest 的使用应该能够满足绝大多数小型项目的测试场景了。但是对于一些涉及数据库交互,网络通信的大型项目的测试场景,我们很难仿真一个真实的环境进行单元测试。所以这时就需要引入 Mock Objects (模拟对象)来模拟程序的一部分来构造测试场景。Mock Object模拟了实际对象的接口,通过一些简单的代码模拟实际对象部分的逻辑,实现起来简单很多。通过 Mock object 的方式可以更好的提升项目的模块化程度,隔离不同的程序逻辑或环境。

至于如何使用 Mock Object 呢?这里要引出本章的主角 Gmock 了,接下来笔者将编写一个简要的 Mock对象并进行单元测试,来展示一下 GMock 的用法。这里我们用 Gmock 模拟一个 kv 存储引擎,并运行一些简单的测试逻辑。下面的代码是我们要模拟的 kv 存储引擎的头文件:

代码语言:txt
AI代码解释
复制
#ifndef LDB_KVDB_MOCK_H
#define LDB_KVDB_MOCK_H

class KVDB {
public:
    std::string get(const std::string &key) const;
    Status set(const std::string &key, const std::string &value);
    Status remove(const std::string &key);
};
#endif //LDB_KVDB_MOCK_H然后我们需要定义个 Mock 类来继承 KVDB,并且定义需要模拟的方法:

class MockKVDB : public KVDB {

public:

代码语言:txt
AI代码解释
复制
MockKVDB() {
代码语言:txt
AI代码解释
复制
}
代码语言:txt
AI代码解释
复制
MOCK_METHOD1(remove, Status(const std::string&));
代码语言:txt
AI代码解释
复制
MOCK_METHOD2(set, Status(const std::string&, const std::string&));
代码语言:txt
AI代码解释
复制
MOCK_METHOD1(get, std::string (const std::string&));

};

代码语言:txt
AI代码解释
复制
通过这个宏定义,我们已经初步模拟出对应的方法了。接下来我们需要告诉 Mock Object 被调用时的正确行为。

TEST_F(TestKVDB, setstr) {

代码语言:txt
AI代码解释
复制
EXPECT_CALL(*kvdb, set(_,_)).WillRepeatedly(Return(Status::SUCCESS));
代码语言:txt
AI代码解释
复制
ASSERT_EQ(kvdb->set("1", "happen"), Status::SUCCESS);
代码语言:txt
AI代码解释
复制
ASSERT_EQ(kvdb->set("2", "lee"), Status::SUCCESS);
代码语言:txt
AI代码解释
复制
ASSERT_EQ(kvdb->set("happen", "1"), Status::SUCCESS);
代码语言:txt
AI代码解释
复制
ASSERT_EQ(kvdb->set("lee", "2"), Status::SUCCESS);

}

TEST_F(TestKVDB, getstr) {

代码语言:txt
AI代码解释
复制
EXPECT_CALL(*kvdb, get(_)) \
代码语言:txt
AI代码解释
复制
        .WillOnce(Return("happen"))
代码语言:txt
AI代码解释
复制
        .WillOnce(Return("lee"))
代码语言:txt
AI代码解释
复制
        .WillOnce(Return("1"))
代码语言:txt
AI代码解释
复制
        .WillOnce(Return("2"));
代码语言:txt
AI代码解释
复制
ASSERT_STREQ(kvdb->get("1").c_str(), "happen");
代码语言:txt
AI代码解释
复制
ASSERT_STREQ(kvdb->get("2").c_str(), "lee");
代码语言:txt
AI代码解释
复制
ASSERT_STREQ(kvdb->get("happen").c_str(), "1");
代码语言:txt
AI代码解释
复制
ASSERT_STREQ(kvdb->get("lee").c_str(), "2");

}

TEST_F(TestKVDB, remove) {

代码语言:txt
AI代码解释
复制
EXPECT_CALL(*kvdb, remove(_)).WillOnce(Return(Status::SUCCESS)).
代码语言:txt
AI代码解释
复制
        WillOnce(Return(Status::FAIL));
代码语言:txt
AI代码解释
复制
EXPECT_CALL(*kvdb, get(_)) \
代码语言:txt
AI代码解释
复制
        .WillOnce(Return(""));
代码语言:txt
AI代码解释
复制
ASSERT_EQ(kvdb->remove("1"), Status::SUCCESS);
代码语言:txt
AI代码解释
复制
ASSERT_EQ(kvdb->get("1"), "");
代码语言:txt
AI代码解释
复制
ASSERT_EQ(kvdb->remove("1"), Status::FAIL);

}

代码语言:txt
复制

由上述代码可以了解,这里通过了EXPECT_CALL来指定 Mock Object 的对应行为,其中 WillOnce代表调用一次返回的结果。通过链式调用的方式,我们就可以构造出所有我们想要的模拟结果。当然如果每次调用的结果都一样,这里也可以使用WillRepeatedly直接返回对应的结果。由上述代码可以看到,这里我们用寥寥数十行代码就模拟出了一个 KV 存储引擎,可见 Gmock 的强大。

这里要注意,在通过 Gmock 来编写 Mock Object 时,能够模拟的方法是对于原抽象类之中的virtual 方法。这个是因为 C++只有通过virtual的方式才能实现子类覆写的多态,这一点在编写代码进行抽象和编写 Mock Object 的时候需要多加注意。

4.小结

通过Gtest 与 Gmock 的使用,能够覆盖绝大多数进行 C++ 单元测试的场景,同时也减少了我们编写单元测试的工作。笔者希望通过本篇文章来抛砖引玉,希望大家多写单测。在笔者实际的工作经验之中,单测给项目带来的影响是极其正面的,一定要坚持写单测,坚持写单测,坚持写单测~~~!!!

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
【可测试性实践】C++单元测试:gtest &amp; gmock
google test是目前C++主流的单元测试框架,本文介绍如何在工程引入gtest和gmock,并提供入门参考示例。根据黄金圈思维我们先思考Why(为什么做),为什么我们要进行单元测试,为什么要引入mock手段来测试代码,然后再来思考How(怎么做),最后思考What(取得了什么效果)。
巫山老妖
2025/06/16
2480
【可测试性实践】C++单元测试:gtest &amp; gmock
gtest和gmock的关系_gtest教程
闲来无事,想尝试一下gtest/gmock,根据下载的源码包里有README,并根据自己安装过程补充记录如下,以便以后查询
全栈程序员站长
2022/11/10
2.1K0
gtest和gmock的关系_gtest教程
gtest整理_softest
::testing::Bool() 会生成 true 和 false两种值,::testing::Combine 把 {true,false} 和 {1, 10} 这两个集合的值组合起来,相当于
全栈程序员站长
2022/09/27
1.6K0
Google Mock介绍
Google Mock是由Google开发的一种用于C++的模拟(mocking)框架,它是Google Test测试框架的一部分。gmock允许开发者创建模拟对象,这些对象可以在单元测试中代替真实的依赖项,使得测试更加灵活和独立。通过使用gmock,开发者可以专注于测试代码逻辑的正确性,而不必担心外部依赖的复杂性。
顾翔
2024/09/10
5690
Google Mock介绍
google软件测试之道_gtest测试框架
gtest 提供了一套优秀的 C++ 单元测试解决方案,简单易用,功能完善,非常适合在项目中使用以保证代码质量。
全栈程序员站长
2022/11/10
8260
google软件测试之道_gtest测试框架
GTest 总结_gtest单元测试
Google C++单元测试框架(简称Gtest),可在多个平台上使用(包括Linux, Mac OS X, Windows, Cygwin和Symbian),它提供了丰富的断言、致命和非致命失败判断,能进行值参数化测试、类型参数化测试、“死亡测试”。
全栈程序员站长
2022/11/11
2.1K0
GTest 总结_gtest单元测试
C++ 项目之Googletest单元测试
Googletest - Google Testing and Mocking Framework
公众号guangcity
2019/09/20
4.3K0
gtest单元测试框架介绍及简单使用
Gtest是Google的一个开源框架,它主要用于写单元测试,检查真自己的程序是否符合预期行为。可在多个平台上使用(包括Linux, Mac OS X, Windows, Cygwin和Symbian)。它提供了丰富的断言、致命和非致命失败判断,能进行值参数化测试、类型参数化测试、“死亡测试”。
杨永贞
2022/04/13
4.3K0
gtest单元测试框架介绍及简单使用
如何用googletest写单元测试
googletest是一个用来写C++单元测试的框架,它是跨平台的,可应用在windows、Linux、Mac等OS平台上。下面,我来说明如何使用最新的1.6版本gtest写自己的单元测试。
陶辉
2019/06/21
6.9K0
如何用googletest写单元测试
GTEST学习总结
https://blog.csdn.net/lm409/article/details/55049893 https://blog.csdn.net/liyuefeilong/article/details/50993607 https://blog.csdn.net/qq_26437925/article/details/56479841
全栈程序员站长
2022/11/11
7960
【gtest】 C++ 的测试框架之使用 gtest 编写单元测试
gtest 是 Google 开发的一个用于 C++ 的测试框架,广泛应用于编写和运行单元测试,并且支持任何类型的测试,而不仅仅是单元测试。
SarPro
2024/06/15
3.1K0
【gtest】 C++ 的测试框架之使用 gtest 编写单元测试
linux下使用gtest框架进行c/c++测试-gmock环境搭建
google mock(以下简称gmock)也是google的开源项目,它能够在测试中起到“打桩”的作用。举个例子,有A模块需要调用B模块,需要对A模块进行测试,但是B模块没有实现,那么就可以利用gmock对B模块进行打桩,来模拟B接口的实现。本文主要介绍gmock环境的搭建,不涉及具体使用。
编程珠玑
2019/09/04
2.6K0
gtest的介绍和使用
顾翔老师开发的bugreport2script开源了,希望大家多提建议。文件在https://github.com/xianggu625/bug2testscript,
顾翔
2019/12/12
1.5K0
gtest的介绍和使用
gtest初识_tests strength
TEST()第一个参数是测试用例的名称,第二个参数是测试用例中的测试名称(有效的C++标识符,不应包含下划线)。 googletest按照测试用例对测试结果进行分组。
全栈程序员站长
2022/11/11
5170
(Android下使用)Google Test C++单元测试框架(一)
gtest是一个跨平台的(Liunx、Mac OS X、Windows、Cygwin、Windows CE and Symbian)C++单元测试框架,由google公司发布。gtest是为在不同平台上为编写C++测试而生成的。它提供了丰富的断言、致命和非致命判断、参数化、”死亡测试”等等。
李小白是一只喵
2020/04/24
3K0
(Android下使用)Google Test C++单元测试框架(一)
gtest框架_软件测试框架
以上命令会将gtest编译好,并将静态链接库放在/usr/local/lib 目录下: zhouhao@ubuntu:/usr/local/lib$ ls libgmock.a libgtest.a pkgconfig python3.6 libgmock_main.a libgtest_main.a python2.7
全栈程序员站长
2022/11/10
2.9K0
Google 单元测试框架
到 github 拉取代码或者下载某个版本的 zip 包到本地目录,参考 gtest 中的 README.md 如何编译库和编译自己的代码,下面简单介绍下编译方法
orientlu
2018/09/13
2.3K0
gtest_gtest测试静态函数
编译: 假设源代码为sample.h和sample.cpp,测试代码为test.cpp
全栈程序员站长
2022/11/11
1.2K0
【gTest】gtest简介及简单使用
gtest是一个跨平台(Liunx、Mac OS X、Windows、Cygwin、Windows CE and Symbian)的C++测试框架,有google公司发布。gtest测试框架是在不同平台上为编写C++测试而生成的。
全栈程序员站长
2022/11/11
2.3K0
linux下使用gtest框架进行c/c++测试-gtest环境搭建
google test(以下简称gtest)是谷歌的开源C++单元测试框架,用来做c/c++的单元测试比较方便。下面对于它在linux下的使用环境搭建做简单介绍。
编程珠玑
2019/09/04
5.8K0
相关推荐
【可测试性实践】C++单元测试:gtest &amp; gmock
更多 >
加入讨论
的问答专区 >
1技术VP擅长5个领域
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档