0


玩转单元测试之gtest

引言

程序开发的时候,往往需要编写一些测试样例来完成功能测试,以保证自己的代码在功能上符合预期,能考虑到一些异常边界问题等等。

gtest快速入门

1.引入gtest

# 使用的是1.10版本,其他版本可根据需要选择git clone -b v1.10.x https://github.com/google/googletest.git
cd googletest
mkdir build &&cd build
cmake ..&&make-j4sudomakeinstallsudo ldconfig

2.编写第一个单测

2.1 待测试文件

#ifndef__HELLO_H__#define__HELLO_H__#include<iostream>#include<string>classAnimal{public:Animal(std::string name):_name(name){}virtual~Animal(){}virtualbooleat(const std::string& food)=0;private:
    std::string _name;};classTigger:publicAnimal{public:Tigger():Animal("tigger"){}booleat(const std::string& food)override{if(food =="meat"){returntrue;}returnfalse;}};classHorse:publicAnimal{public:Horse():Animal("Horse"){}booleat(const std::string& food)override{if(food =="grass"){returntrue;}returnfalse;}};#endif

2.2 单测文件

#include"hello.h"#include"gtest/gtest.h"usingnamespace::testing;namespace{TEST(TestTigger, CaseEat){
    Animal *tigger =newTigger();bool ret = tigger->eat("meat");EXPECT_TRUE(ret);

    ret = tigger->eat("grass");EXPECT_FALSE(ret);delete tigger;}TEST(TestHorse, CaseEat){
    Animal *horse =newHorse();bool ret = horse->eat("grass");EXPECT_TRUE(ret);

    ret = horse->eat("meat");EXPECT_FALSE(ret);delete horse;}}

2.3 makefile文件

CXX = g++
CXXFLAGS = -Wall
LIBES = -lgtest -lgtest_main -lpthread
LPATH = -L/tools/googletest/1.11.0/build/lib  # 替换成自己lib路径
HPATH = -I/tools/googletest/1.11.0/googletest/include/ # 替换成自己的include路径

UTEST_OBJD = hello_unit_test

hello_unit_test:hello_unit_test.cpp
    ${CXX}-o $@ $+-I ../ ${HPATH} ${CXXFLAGS} ${LIBES} ${LPATH}

clean:
    rm-rf *_unit_test

make && ./hello_unit_test 编译并执行单测程序,执行结果如下:
在这里插入图片描述

gtest常用宏

1. 各种断言

1.1 Bool断言

致命断言非致命断言含义ASSERT_TRUE(val)EXPECT_TRUE(val)val == trueASSERT_FALSE(val)EXPECT_FALSE(val)val == false

1.2 二元值断言(比较大小)

致命断言非致命断言含义ASSERT_EQ(a, b)EXPECT_EQ(a, b)a == bASSERT_NE(a, b)EXPECT_NE(a, b)a != bASSERT_LT(a, b)EXPECT_LT(a, b)a < bASSERT_LE(a, b)EXPECT_LE(a, b)a <= bASSERT_GT(a, b)EXPECT_GT(a, b)a > bASSERT_GE(a, b)EXPECT_GE(a, b)a >= b

1.3 字符串断言(字符串比较)

致命断言非致命断言含义ASSERT_STREQ(a, b)EXPECT_STREQ(a, b)a == bASSERT_STRNE(a, b)EXPECT_STRNE(a, b)a != bASSERT_STRCASEEQ(a, b)EXPECT_STRCASEEQ(a, b)a == b 忽略大小写ASSERT_STRCASENE(a, b)EXPECT_STRCASENE(a, b)a != b 忽略大小写

2. TEST、TEST_F和TEST_P

2.1 TEST

TEST是最基本的构造测试case的宏,基本用法:

TEST(param1, prama2){/*测试代码*/}
  • 参数1:用例名,一般由待测试的类名或函数名组成,如TestAnimal
  • 参数2:测试名,代表测试含义,如CaseEat
  • 测试结果将以"用例名.测试名"来区分不同测试case

2.2 TEST_F

TEST_F和TEST的不同之处在于,其可以使用到初始化函数(SetUp)和一个清理函数(TearDown)。基本用法如下:

classTestAnimal:public::testing::Test{protected:voidSetUp()override{// 成员变量初始化
        tigger =newTigger();}voidTearDown()override{// 资源清理、释放delete tigger;
        tigger =NULL;}protected:
    Animal *tigger;};TEST_F(TestAnimal, caseEatMeat){// 执行之前调用SetUp进行初始化EXPECT_TRUE(tigger->eat("meat"));// case退出时调用TearDown进行释放}TEST_F(TestAnimal, caseEatGrass){// 执行之前调用SetUp进行初始化EXPECT_FALSE(tigger->eat("grass"));// case退出时调用TearDown进行释放}
  • 创建一个继承testing::Test的测试类TestAnimal,并在该类中声明成员变量,做好初始化和清理操作
  • TEST_F宏 - 参数1:同测试类名(TestAnimal )- 参数2:测试名,代表测试含义
  • 每一个测试case都是相互独立的,当每个case需要共同使用某个变量时,可以将该变量放在测试类中,每执行一个TEST_F宏构造的case,都会调用一次SetUp和TearDown,因此case之间对变量的操作不会相互影响

2.3 TEST_P

针对某个待测试的方法,当你需要测试不同的输入,但又不想每个case都写一遍时,就可以使用到TEST_P宏,基本使用如下:

// 多个参数时,使用结构体更方便structMyParams{
    std::string food;// other params};classTestAnimal:public::testing::Test,public::testing::WithParamInterface<MyParams>{protected:voidSetUp()override{// 成员变量初始化
        tigger =newTigger();}voidTearDown()override{// 资源清理、释放delete tigger;
        tigger =NULL;}protected:
    Animal *tigger;};TEST_P(TestAnimal, caseEat){
    std::string food =GetParam().food;// 获取参数ASSERT_FALSE(tigger->eat(food));}// 构造不同的测试样例INSTANTIATE_TEST_SUITE_P(TestCaseEatParams, TestAnimal,::testing::Values(
    MyParams{"grass"},
    MyParams{"leafs"}));
  • 和TEST_F有相似的功能,使用SetUp、TearDown进行初始化和清理,创建一个继承testing::Test、testing::WithParamInterface的测试类,其中WithParamInterface是一个模板类,用来关联测试参数。
  • TEST_P宏 - 参数1:测试类名- 参数2:测试名,代表测试含义
  • INSTANTIATE_TEST_SUITE_P宏 - 参数1:能表明测试含义即可- 参数2:测试类名- 参数3:不同测试样例集合
  • 执行结果如下:在这里插入图片描述

总结

  • 好记性不如烂笔头,最近在写单元测试,于是就有了这篇文章。
  • 通过学习gtest的基本语法,已经可以应对一部分测试场景了,然而还有一些场景只通过gtest是无法完成的,比如,在我们的代码中有许多并不是我们自己设计的接口,可能是外部依赖,也可能来自于其他模块,我们没办法设计一个合适的case来让这些接口返回给我们一个预期值,那我们该怎么办呢?于是gmock由此诞生,这个在下一篇中会进行深入学习,篇名我已经想好了(玩转单元测试之GMock)
标签: 单元测试 c++

本文转载自: https://blog.csdn.net/weixin_43519984/article/details/132336675
版权归原作者 Black.Spider 所有, 如有侵权,请联系我们删除。

“玩转单元测试之gtest”的评论:

还没有评论