我的编程空间,编程开发者的网络收藏夹
学习永远不晚

C/C++中gtest怎么用

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

C/C++中gtest怎么用

这篇文章主要介绍了C/C++中gtest怎么用,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。

Google C++ Testing Framework(简称gtest,http://code.google.com/p/googletest/)是Google公司发布的一个开源C/C++单元测试框架,已被应用于多个开源项目及Google内部项目中,知名的例子包括Chrome Web浏览器、LLVM编译器架构、Protocol Buffers数据交换格式及工具等。

优秀的C/C++单元测试框架并不算少,相比之下gtest仍具有明显优势。与CppUnit比,gtest需要使用的头文件和函数宏更集中,并支持测试用例的自动注册。与CxxUnit比,gtest不要求Python等外部工具的存在。与Boost.Test比,gtest更简洁容易上手,实用性也并不逊色。Wikipedia给出了各种编程语言的单元测试框架列表(http://en.wikipedia.org/wiki/List_of_unit_testing_frameworks)。

一、基本用法

gtest当前的版本是1.5.0,如果使用Visual C++编译,要求编译器版本不低于7.1(Visual C++ 2003)。如下图所示,它的msvc文件夹包含Visual C++工程和项目文件,samples文件夹包含10个使用范例。

C/C++中gtest怎么用

一般情况下,我们的单元测试代码只需要包含头文件gtest.h。gtest中常用的所有结构体、类、函数、常量等,都通过命名空间testing访问,不过gtest已经把最简单常用的单元测试功能包装成了一些带参数宏,因此在简单的测试中常常可以忽略命名空间的存在。

按照gtest的叫法,宏TEST为特定的测试用例(Test Case)定义了一个可执行的测试(Test)。它接受用户指定的测试用例名(一般取被测对象名)和测试名作为参数,并划出了一个作用域供填充测试宏语句和普通的C++代码。一系列TEST的集合就构成一个简单的测试程序。

常用的测试宏如下表所示。以ASSERT_开头和以EXPECT_开头的宏的区别是,前者在测试失败时会给出报告并立即终止测试程序,后者在报告后继续执行测试程序。

ASSERT

EXPECT

功能

ASSERT_TRUE

EXPECT_TRUE

判真

ASSERT_FALSE

EXPECT_FALSE

判假

ASSERT_EQ

EXPECT_EQ

相等

ASSERT_NE

EXPECT_NE

不等

ASSERT_GT

EXPECT_GT

大于

ASSERT_LT

EXPECT_LT

小于

ASSERT_GE

EXPECT_GE

大于或等于

ASSERT_LE

EXPECT_LE

小于或等于

ASSERT_FLOAT_EQ

EXPECT_FLOAT_EQ

单精度浮点值相等

ASSERT_DOUBLE_EQ

EXPECT_DOUBLE_EQ

双精度浮点值相等

ASSERT_NEAR

EXPECT_NEAR

浮点值接近(第3个参数为误差阈值)

ASSERT_STREQ

EXPECT_STREQ

C字符串相等

ASSERT_STRNE

EXPECT_STRNE

C字符串不等

ASSERT_STRCASEEQ

EXPECT_STRCASEEQ

C字符串相等(忽略大小写)

ASSERT_STRCASENE

EXPECT_STRCASENE

C字符串不等(忽略大小写)

ASSERT_PRED1

EXPECT_PRED1

自定义谓词函数,(pred, arg1)(还有_PRED2, ..., _PRED5

写个简单的测试试一下。假设我们实现了一个加法函数:

// add.h  #pragma once  inline int Add(int i, int j) { return i+j; }

对应的单元测试程序可以这样写:

// add_unittest.cpp  #include "add.h"  #include <gtest/gtest.h>     TEST(Add, 负数) {   EXPECT_EQ(Add(-1,-2), -3);   EXPECT_GT(Add(-4,-5), -6); // 故意的  }     TEST(Add, 正数) {   EXPECT_EQ(Add(1,2), 3);   EXPECT_GT(Add(4,5), 6);  }

代码中,测试用例Add包含两个测试,正数和负数(这里利用了Visual C++ 2005以上允许标识符包含Unicode字符的特性)。编译运行效果如下:

在控制台界面中,通过的测试用绿色表示,失败的测试用红色表示。双横线分隔了不同的测试用例,其中包含的每个测试的启动与结果用单横线和RUN ... OK或RUN ... FAILED标出。失败的测试会打印出代码行和原因,测试程序***为所有用例和测试显示统计结果。建议读者试一下换成ASSERT_宏的不同之处。

每个测试宏还可以使用<<运算符在测试失败时输出自定义信息,如:

ASSERT_EQ(M[i], N[j]) << "i = " << i << ", j = " << j;

编译命令行中,gtest_mt.lib和gtest_main_mt.lib就是前面使用VC项目文件生成的静态库。有意思的是,测试代码不需要注册测试用例,也不需要定义main函数,这是gtest通过后一个静态库自动完成的,它的实现代码如下:

// gtest-main.cc  int main(int argc, char **argv) {   std::cout << "Running main() from gtest_main.cc\n";   testing::InitGoogleTest(&argc, argv);   return RUN_ALL_TESTS();  }

其中,函数InitGoogleTest负责注册需要运行的所有测试用例,宏RUN_ALL_TEST负责执行所有测试,如果全部成功则返回0,否则返回1。当然,我们也可以仅链接gtest_mt.lib,自己提供main函数。

二、测试固件

很多时候,我们想在不同的测试执行前创建相同的配置环境,在测试执行结束后执行相应的清理工作,测试固件(Test Fixture)为这种需求提供了方便。“Fixture”是一个汉语中不易直接对应的词,《美国传统词典》对它的解释是“(作为附属物的)固定装置;被固定的状态”。在单元测试中,Fixture的作用是为测试创建辅助性的上下文环境,实现测试的初始化和终结与测试过程本身的分离,便于不同测试使用相同代码来搭建固定的配置环境。用体操比赛的说法,测试过程体现了特定测试的自选动作,测试固件则体现了对一系列测试(在开始和结束时)的规定动作。有些讲单元测试的书籍直接把测试固件称为Scaffolding(脚手架)。

使用测试固件比单纯调用TEST宏稍微麻烦一些:

         从gtest的testing::Test类派生一个类,用public或protected定义以下所有成员。

         (可选)建立环境:使用默认构造函数,或定义一个虚成员函数virtual void SetUp()。

         (可选)销毁环境:使用析构函数,或定义一个虚成员函数virtual void TearDown()。

         用TEST_F定义测试,写法与TEST相同,但测试用例名必须为上面定义的类名。

每个带固件的测试的执行顺序是:

         调用默认构造函数创建一个新的带固件对象。

         立即调用SetUp函数。

         运行TEST_F体。

         立即调用TearDown函数。

         调用析构函数销毁类对象。

C/C++中gtest怎么用

从gtest的实现代码可以看到,TEST_F又从用户定义的类自动派生了一个类,因此要求public或protected的访问权限;大括号里的内容被扩展成一个名为TestBody的虚成员函数的函数体,因此可以在其中直接访问成员变量和成员函数。其实TEST也采用了相同的实现机制,只是它直接从gtest的testing::Test自动派生类,所以可以指定任意用例名。testing::Test类的SetUp和TearDown都是空函数,所以它只执行测试步骤,没有环境的创建和销毁。

借用上面Add函数写个固件测试的例子:

// add_unittest2.cpp  #include "add.h"  #include <stdio.h>  #include <gtest/gtest.h>     class AddTest: public testing::Test  {  public:   virtual void SetUp()    { puts("SetUp()"); }   virtual void TearDown() { puts("TearDown()"); }  };     TEST_F(AddTest, 正数) {   ASSERT_GT(Add(1,2), 3); // 故意的   ASSERT_EQ(Add(4,5), 6); // 也是故意的  }

编译运行效果如下:

必须强调,每个TEST_F开始都创建了一个新的带固件对象,因此每个测试都使用独立的完全相同的初始环境,各测试可以按任意顺序执行(参见--gtest_shuffle命令行选项)。但在某些情况下,我们可能需要在各个测试间共享一个相同的环境来保存和传递状态,或者环境的状态是只读的,可以只初始化一次,再或者创建环境的过程开销很高,要求只初始化一次。共享某个固件环境的所有测试合称为一个“测试套件”(Test Suite),gtest中利用静态成员变量和静态成员函数实现这个概念:

         (可选)在testing::Test的派生类中,定义若干静态成员变量来维护套件的状态。

         (可选)建立共享环境:定义一个静态成员函数static void SetUpTestCase()。

         (可选)销毁共享环境:定义一个静态成员函数static void TearDownCase()。

另外,还可以使用gtest的Environment类来建立和销毁所有测试共用的全局环境(对应于上图显示的“Global test environment set-up”和“Global test environment tear-down”):

class Environment {   public:   virtual ~Environment() {}   virtual void SetUp() {}   virtual void TearDown() {}  };

gtest文档建议测试程序自己定义main函数并在其中创建和注册全局环境对象:

Environment* AddGlobalTestEnvironment(Environment* env);

三、异常测试

C程序中要返回出错信息,可以利用特定的函数返回值、函数的输出(outbound)参数、或者设置全局变量(如C标准库定义的errno,Windows API中的“上次错误”(last error)代码,Winsock中与每个socket相关联的错误代码)。C++程序常用异常(exception)来返回出错信息,gtest为异常测试提供了专用的测试宏:

ASSERT

EXPECT

功能

ASSERT_NO_THROW

EXPECT_NO_THROW

不抛出异常,参数为(statement)

ASSERT_ANY_THROW

EXPECT_ANY_THROW

抛出异常,参数为(statement)

ASSERT_THROW

EXPECT_THROW

抛出特定类型的异常,参数为(statement, type)

需要注意,这些测试宏都接受C/C++语句作为参数,所以既可以像前面那样传递表达式,也可以传递用大括号包起来的代码块。

借助下面的被测函数:

// divide.h  #pragma once  #include <stdexcept>     int divide(int dividend, int divisor) {   if(!divisor) {      throw std::length_error("can't be divided by 0"); // 故意的   }   return dividend / divisor;  }

测试程序如下:

// divide-unittest.cpp  #include <gtest/gtest.h>  #include "./divide.h"     TEST(Divide, ByZero) {   EXPECT_NO_THROW(divide(-1, 2));      EXPECT_ANY_THROW({      int k = 0;      divide(k, k);   });      EXPECT_THROW(divide(100000, 0), std::invalid_argument);  }

编译运行效果如下

C/C++中gtest怎么用

容易想到,gtest的这些异常测试宏是用C++的try ... catch语句来实现的:

try {   statement;  }  catch(type const&) {   // throw  }  catch(...) {   // any throw  }  // no throw

如果把上图中Visual C++的编译选项/EHsc换成/EHa,try ... catch就可以同时支持C++风格的异常和Windows系统的结构化异常(SEH)。这样,即使删掉divide函数里的if判断,测试代码的EXPECT_ANY_THROW宏也会成功捕获异常。

遗憾的是,目前仅使用这些测试宏无法得到获得被抛出异常的详细信息(如divide函数中的报错文本),这和gtest自身不愿意使用C++异常有关。

四、值参数化测试

有些时候,我们需要对代码实现的功能使用不同的参数进行测试,比如使用大量随机值来检验算法实现的正确性,或者比较同一个接口的不同实现之间的差别。gtest把“集中输入测试参数”的需求抽象出来提供支持,称为值参数化测试(Value Parameterized Test)。

值参数化测试包括4个步骤:

         从gtest的TestWithParam模板类派生一个类(记为C),模板参数为需要输入的测试参数的类型。由于TestWithParam本身是从Test派生的,所以C就成了一个测试固件类。

         在C中,可以实现诸如SetUp、TearDown等方法。特别地,测试参数由TestWithParam实现的GetParam()方法依次返回。

         使用TEST_P(而不是TEST_F)定义测试。

         使用INSTANTIATE_TEST_CASE_P宏集中输入测试参数,它接受3个参数:任意的文本前缀,测试类名(这里即为C),以及测试参数值序列。gtest框架依次使用这些参数值生成测试固件类实例,并执行用户定义的测试。

gtest提供了专门的模板函数来生成参数值序列,如下表所示:

参数值序列生成函数

含义

Bool()

生成序列{false, true}

Range(begin, end[, step])

生成序列{begin, begin+step, begin+2*step, ...} (不含end),step默认为1

Values(v1, v2, ..., vN)

生成序列{v1, v2, ..., vN}

ValuesIn(container)ValuesIn(iter1, iter2)

枚举STL container,或枚举迭代器范围[iter1, iter2)

Combine(g1, g2, ..., gN)

生成g1g2, ..., gN的笛卡尔积,其中g1g2, ..., gN均为参数值序列生成函数(要求C++0x的<tr1/tuple>

写个小程序试一下。假设我们实现了一种快速累加算法,希望使用另一种直观算法进行正确性校验。算法实现和测试代码如下

// addupto.h     #pragma once     inline unsigned NaiveAddUpTo(unsigned n) {      unsigned sum = 0;      for(unsigned i = 1; i <= n; ++i) sum += i;      return sum;  }     inline unsigned FastAddUpTo(unsigned n) {      return n*(n+1)/2;  }

测试程序如下:

// addupto_test.cpp     #include <gtest/gtest.h>  #include "addupto.h"     class AddUpToTest : public testing::TestWithParam<unsigned>  {  public:      AddUpToTest() { n_ = GetParam(); }  protected:      unsigned n_;  };     TEST_P(AddUpToTest, Calibration) {      EXPECT_EQ(NaiveAddUpTo(n_), FastAddUpTo(n_));  }     INSTANTIATE_TEST_CASE_P(      NaiveAndFast, // prefix      AddUpToTest,   // test case name      testing::Range(1u, 1000u) // parameters  );

注意TestWithParam的模板参数设置为unsigned类型,而在代码倒数第2行,两个常量值都加了u后缀来指定为unsigned类型。熟悉C++的读者应该知道,模板函数在进行类型推断(deduction)时匹配相当严格,不像普通函数那样允许类型提升(promotion)。如果上面省略u后缀,就会造成编译错误。当然还可以显式指定模板参数:testing::Range<unsigned>(1, 1000)。

运行效果如下,这里省略了开头的大部分输出(命令行窗口设置的缓冲区高度为3000行)。

C/C++中gtest怎么用

感谢你能够认真阅读完这篇文章,希望小编分享的“C/C++中gtest怎么用”这篇文章对大家有帮助,同时也希望大家多多支持编程网,关注编程网行业资讯频道,更多相关知识等着你来学习!

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

C/C++中gtest怎么用

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

C/C++中gtest怎么用

这篇文章主要介绍了C/C++中gtest怎么用,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。Google C++ Testing Framework(简称gtest,http
2023-06-17

C++/C中#define怎么使用

本篇内容介绍了“C++/C中#define怎么使用”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!1 缩减代码 第一种用法与typedef类似
2023-07-02

C++中怎么调用C接口

本篇文章为大家展示了C++中怎么调用C接口,内容简明扼要并且容易理解,绝对能使你眼前一亮,通过这篇文章的详细介绍希望你能有所收获。C++如何调用C接口首先提供一个C接口:#include"test.h" void testCfun() {
2023-06-16

c++中!怎么用

c++ 中惊叹号 (!) 表示逻辑非运算,将布尔值取反:如果值为真,返回假;如果值为假,返回真。C++ 中的惊叹号 (!) 用法在 C++ 中,惊叹号 (!) 操作符表示逻辑非运算。它的作用是将一个布尔值(true 或 false)取反。
c++中!怎么用
2024-04-26

c++中::怎么用

在 c++ 中,:: 运算符用于访问类的静态成员或全局变量。它可以让您访问静态成员,即使没有类实例,也可以访问全局变量,即使没有源文件。:: 在 C++ 中的用途:: 是 C++ 中的一个运算符,称为域解决运算符。它用于访问类的静态成员和
c++中::怎么用
2024-04-26

C/C++中的#define怎么使用

这篇文章主要介绍“C/C++中的#define怎么使用”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“C/C++中的#define怎么使用”文章能帮助大家解决问题。1.数值类型输出易读的字符串形式例如
2023-07-05

c++中?:怎么用

条件运算符(?:)在 c++ 中可根据条件执行不同操作。语法:condition ? true_expression : false_expression。其中,condition 是布尔表达式,确定执行true_expression或fa
c++中?:怎么用
2024-04-26

C++中怎么调用C链接库

本篇文章给大家分享的是有关C++中怎么调用C链接库,小编觉得挺实用的,因此分享给大家学习,希望大家阅读完这篇文章后可以有所收获,话不多说,跟着小编一起来看看吧。C++调用C链接库,其实相对C调用C++。因为C++本来就向下兼容C吧但由于编译
2023-06-17

C#中dialogresult.ok怎么用

在C#中,DialogResult.OK用于指示对话框的结果为"确定"。通常,可以在对话框的按钮的Click事件中使用DialogResult.OK来设置对话框的结果。以下是一个示例:private void btnOk_Click(ob
C#中dialogresult.ok怎么用
2024-02-29

c++中define怎么用

define 是 c++ 预处理器指令,用于定义宏,是一个名称,代表值或代码块。使用方法:预处理器宏:引用宏名称替换为值或代码块。宏函数:宏定义为函数,使用宏名() 调用。优点:缩写代码。定义常量。条件编译。注意事项:命名空间问题。宏副作用
c++中define怎么用
2024-05-12

c++中setw怎么用

setw() 函数用于设置 c++ 中输出字段的最小宽度,允许指定每个值的右对齐和填充空格。其语法为 ostream& setw(int width),参数为要设置的字符宽度。它仅影响当前输出操作,后续输出不受影响,并且可以与其他格式化标志
c++中setw怎么用
2024-04-28

C++中decltype怎么用

这篇文章给大家分享的是有关C++中decltype怎么用的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。1.语法decltype ( 实体 ) (1) (C++11 起)decltype ( 表达式 ) (2) (
2023-06-21

C#中Dapper怎么用

小编给大家分享一下C#中Dapper怎么用,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!一、什么是Dapper  Dapper是一款轻量级ORM工具(Github
2023-06-20

c#中token怎么用

这篇文章主要介绍了c#中token怎么用,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。token的存在意义这是我初略了解的token的存在意义用户使用用户名密码来请求服务器服
2023-06-29

c++中strcpy_s怎么用

strcpy_s 函数用于安全地将字符串 src 复制到字符串 dst 中,其语法为:检查参数的有效性。调用 strcpy_s,将 src 复制到 dst 中。检查返回值以确保复制成功或目标字符串是否太小。strcpy_s函数在 C++ 中
c++中strcpy_s怎么用
2024-05-08

c++中std::怎么用

std 是 c++ 中包含标准库组件的命名空间。为了使用 std,需要使用 "using namespace std;" 语句。直接使用 std 命名空间中的符号可以简化代码,但建议仅在需要时使用,以避免命名空间污染。std 在 C++ 中
c++中std::怎么用
2024-05-09

c++中counter怎么用

c++ 中的 counter 是一个 stl 容器,用于存储和计数不同的值。它使用整型键和值,通过 [] 运算符插入或更新值,提供遍历、查找最大值和排序元素等操作。例如,可以用来统计单词出现的次数。理解 C++ 中的 counterC++
c++中counter怎么用
2024-04-26

c++中setfill怎么用

setfill 是 c++ 流操作器的成员函数,用于设置流中未填充字符的填充字符,从而填充插入操作符产生的未填充字段。语法:ostream& setfill(char ch),其中 ch 是要设置为填充字符的字符。示例:setfill(''
c++中setfill怎么用
2024-05-01

c++中bool怎么用

bool 类型是 c++ 中用于表示布尔值(真或假)的数据类型。具体使用方法如下:包含 和 头文件。声明一个 bool 变量,如:bool my_bool;。将 true 或 false 赋值给变量,如:my_bool = true;。
c++中bool怎么用
2024-05-01

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录