gtest和gmock在C++中的使用过程

本文详细介绍了如何使用gtest和gmock进行单元测试,包括安装步骤、基本宏的使用以及测试函数和类的方法。通过一系列的Demo演示了TEST和TEST_F宏的运用,并给出了gmock的简单应用。文章强调了测试在开发中的重要性,并提供了实验代码和编译指南。
摘要由CSDN通过智能技术生成

1. 介绍

好的开发必然是一个好的测试.
而业界普遍使用的测试框架是gtest, 测试一个方法A时涉及到了其他的类B, 可以通过gmock来模拟其他类B实现输入输出

本文介绍如何使用gtest和gmock, 具体的实验demo已经上传到了国产github, 有需要的点击下载, 如果真的帮助到你, 请给个star

git clone https://gitee.com/nwu_zjq/demo_gtest_gmock.git

2. 源码编译安装

克隆下给出的demo, 里面有gtest的安装包
需要的环境有 cmake, gcc, 最好gdb也有

# export MYPATH=<指定安装目录如:/home/install>
export MYPATH=/home/install
mkdir -p ${MYPATH}
unzip source/googletest-main.zip
mkdir -p source/googletest-main/build && pushd source/googletest-main/build
cmake .. -DCMAKE_INSTALL_PREFIX=${MYPATH} && make -j4 && sudo make install
echo "export CPLUS_INCLUDE_PATH=CPLUS_INCLUDE_PATH:${MYPATH}/include" >> ~/.bashrc
echo "export C_INCLUDE_PATH=C_INCLUDE_PATH:${MYPATH}/include" >> ~/.bashrc
echo "export OBJC_INCLUDE_PATH=OBJC_INCLUDE_PATH:${MYPATH}/include" >> ~/.bashrc
echo "export LIBRARY_PATH=${MYPATH}/lib:$LIBRARY_PATH" >> ~/.bashrc
echo "export LD_LIBRARY_PATH=${MYPATH}/lib:$LD_LIBRARY_PATH" >> ~/.bashrc
echo "export PKG_CONFIG_PATH=${MYPATH}/lib/pkgconfig:$PKG_CONFIG_PATH" >> ~/.bashrc
pushd
source ~/.bashrc

3. demo

3.1. Demo1: 简单的利用gtest给定的一些宏进行测试

Demo1:EXPECT

本实例是通过Makefile编译运行测试用例的, 由于上面安装过程中, 已经将gtest的库包含到了系统环境变量中, 因此在代码连接过程中, 直接使用-lgtest -lgtest_main 即可, 这里也是我一直犯得错误

  • 1 测试代码
// test_main.cpp
#include <gmock/gmock.h>
#include <gtest/gtest.h>

int add(int a, int b){
    return a+b;
}

int main(int argc, char const *argv[]) {
    EXPECT_EQ(add(1, 2), 3); // pass
    EXPECT_EQ(add(1, 2), 1) << "FAILED: EXPECT: 2, but given 1";; // FAILDED
    return 0;
}
  • 管理编译运行
# Makefile
# 与test_main.cpp同个文件夹, 执行命令是make
CXX=g++
# LDFLAGS = -g -L/home/zjq/01_software/install/lib
LIBS = -lgtest -lgtest_main -lpthread
# CXXFLAGS = -I/home/zjq/01_software/install/include
test_main: test_main.o
	$(CXX) -o test_main test_main.o $(LIBS)
	./test_main 
test_main.o: test_main.cpp
	$(CXX) -c test_main.cpp
clean:
	rm -rf *.o test_main
  • 运行结果
# 运行结果
$ ./test_main 
test_main.cpp:10: Failure
Expected equality of these values:
  add(1, 2)
    Which is: 3 # 期待的值
  1				# 给定的值
FAILED: EXPECT: 2, but given 1 # 自己添加的提示信息 

3.2 gtest给的一些宏

开始

表1 一元比较

ASSERTEXPECTVerifies
ASSERT_TRUE(condition);EXPECT_TRUE(condition);condition is true
ASSERT_FALSE(condition)EXPECT_FALSE(condition)condition is false

表2 二元比较

ASSERTEXPECTCondition
ASSERT_EQ(val1, val2);EXPECT_EQ(val1, val2);val1 == val2
ASSERT_NE(val1, val2);EXPECT_NE(val1, val2);val1 != val2
ASSERT_LT(val1, val2);EXPECT_LT(val1, val2);val1 < val2
ASSERT_LE(val1, val2);EXPECT_LE(val1, val2);val1 <= val2
ASSERT_GT(val1, val2);EXPECT_GT(val1, val2);val1 > val2
ASSERT_GE(val1, val2);EXPECT_GE(val1, val2);val1 >= val2

字符串检查

Fatal assertionNonfatal assertionVerifies
ASSERT_STREQ(expected_str, actual_str);EXPECT_STREQ(expected_str,actual_str);the two C strings have the same content
ASSERT_STRNE(str1, str2);EXPECT_STRNE(str1, str2);the two C strings have different content
ASSERT_STRCASEEQ(expected_str, actual_str);EXPECT_STRCASEEQ(expected_str, actual_str);the two C strings have the same content, ignoring case (忽略大小写)
ASSERT_STRCASENE(str1, str2);EXPECT_STRCASENE(str1, str2);the two C strings have different content, ignoring case (忽略大小小)

异常检查

Fatal assertionNonfatal assertionVerifies
ASSERT_THROW(statement, exception_type);EXPECT_THROW(statement, exception_type);statement throws an exception of the given type
ASSERT_ANY_THROW(statement);EXPECT_ANY_THROW(statement);statement throws an exception of any type
ASSERT_NO_THROW(statement);EXPECT_NO_THROW(statement);statement doesn't throw any exception

3.3 利用gtest测试方法和类

本实验使用TEST宏和TEST_F宏, 来实现对函数, 类的测试
并且通过CmakeLists.txt代替Makefile来管理自动化编译和运行

a. Demo2: TEST

Demo2: TEST

  • 编写对函数的测试用例, gtest会按照顺序进行执行测试
// test_main.cpp
#include <gmock/gmock.h>
#include <gtest/gtest.h>
// 直接在当前目录运行 make

int Factorial(int n) {
  int result = 1;
  for (int i = 1; i <= n; i++) {
    result *= i;
  }

  return result;
}

// 正数为一组
TEST(FactorialTest, Negative) {
  EXPECT_EQ(1, Factorial(-5));
  EXPECT_EQ(1, Factorial(-1));
  EXPECT_GT(Factorial(-10), 0);
}
// 0
TEST(FactorialTest, Zero) {
  EXPECT_EQ(1, Factorial(0));
  EXPECT_EQ(3, Factorial(0)); // 故意设定一个错误的
}
// 负数为一组
TEST(FactorialTest, Positive) {
  EXPECT_EQ(1, Factorial(1));
  EXPECT_EQ(2, Factorial(2));
  EXPECT_EQ(6, Factorial(3));
  EXPECT_EQ(40320, Factorial(8));
}

int main(int argc, char **argv) {
  printf("Running main() from %s\n", __FILE__);
  testing::InitGoogleTest(&argc, argv);
  return RUN_ALL_TESTS(); 
}
  • 编写CMakeLists.txt文件, 完成自动编译运行
# CMakeLists.txt
# 设置变量
set(GTESTLIB gtest gtest_main pthread) 
# 生成可执行文件
add_executable(test_main test_main.cpp)
# 指定链接动态库或静态库, 链接导入库
target_link_libraries(test_main ${GTESTLIB}) 
  • 编译运行
mkdir -p build && cd build && cmake .. && make -j4

# 运行结果
./test_main
Running main() from test_main.cpp
[==========] Running 3 tests from 1 test suite. # 3组测试用例
[----------] Global test environment set-up.
[----------] 3 tests from FactorialTest
[ RUN      ] FactorialTest.Negative             # Negative 组输出
[       OK ] FactorialTest.Negative (0 ms)      # OK 表示 Negative 组全部测试通过
[ RUN      ] FactorialTest.Zero                 # Zero组输出 
[       OK ] FactorialTest.Zero (0 ms)
[ RUN      ] FactorialTest.Positive             # Positive组输出
[       OK ] FactorialTest.Positive (0 ms)
[----------] 3 tests from FactorialTest (0 ms total)

                                                # sample1_unitest 另一个测试案例的输出 ...
[----------] Global test environment tear-down
[==========] 3 tests from 1 test suite ran. (0 ms total)
[  PASSED  ] 3 tests.                           # 全部测试结果:PASS表示全部通过 

b. Demo3: TEST_F

Demo3: TEST_F

#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <iostream>
#include "sample3-inl.h" // 定义并实现了Queue

using namespace std;


// 直接在当前目录运行 make
class QueueTestSmpl3 : public testing::Test { // 继承了 testing::Test
protected:  
  
  static void SetUpTestSuite() {
    cout<< "TestSuite测试套事件: 在第一个testcase之前执行" << endl;
  } 

  static void TearDownTestSuite() {
    cout<< "TestSuite测试套事件: 在第一个testcase之后执行" << endl;
  }
  
  virtual void SetUp() override { // 这里初始化对象
    cout<< "TestSuite 测试用例 事件: 在每个testcase之前执行" << endl;
    q1_.Enqueue(1);
    q2_.Enqueue(2);
    q2_.Enqueue(3);
  }

  virtual void TearDown() override { // 在这里析构对象
    cout<< "TestSuite 测试用例 事件: 在每个testcase之后执行" << endl;
  }
  
  static int Double(int n) {
    return 2*n;
  }
  
  void MapTester(const Queue<int> * q) {
    const Queue<int> * const new_q = q->Map(Double);

    ASSERT_EQ(q->Size(), new_q->Size());

    for (const QueueNode<int>*n1 = q->Head(), *n2 = new_q->Head();
         n1 != nullptr; n1 = n1->next(), n2 = n2->next()) {
      EXPECT_EQ(2 * n1->element(), n2->element());
    }

    delete new_q;
  }

  Queue<int> q0_;
  Queue<int> q1_;
  Queue<int> q2_;
};

class FooEnvironment : public testing::Environment {
public:
    virtual void SetUp(){
        cout<< "FooEnvironment SetUp" << endl;
    }

    virtual void TearDown(){
        cout<< "FooEnvironment TearDown" << endl;

    }    
};

// in sample3_unittest.cc

/* TEST_F执行流程
1. gtest 构造对象 t1
2. t1.SetUp 初始化t1
3. 下面的TEST_F运行并结束
4. t1.TearDown 运行用于清理工作
5. t1 被析构
*/
// Tests the default c'tor.
TEST_F(QueueTestSmpl3, DefaultConstructor) {
  // !!! 在 TEST_F 中可以使用 QueueTestSmpl3 的成员变量、成员函数 
  EXPECT_EQ(0u, q0_.Size()); // q0_数量是0
}

// Tests Dequeue().
TEST_F(QueueTestSmpl3, Dequeue) {
  int * n = q0_.Dequeue(); // q0_数量是0
  EXPECT_TRUE(n == nullptr);

  n = q1_.Dequeue(); // // q1_ pop出的是1=>*n
  ASSERT_TRUE(n != nullptr);
  EXPECT_EQ(1, *n);
  EXPECT_EQ(0u, q1_.Size()); // q1_ 此时是size=0
  delete n;

  n = q2_.Dequeue();
  ASSERT_TRUE(n != nullptr);
  EXPECT_EQ(2, *n);
  EXPECT_EQ(1u, q2_.Size());
  delete n;
}

// Tests the Queue::Map() function.
TEST_F(QueueTestSmpl3, Map) {
  MapTester(&q0_);
  MapTester(&q1_);
  MapTester(&q2_);
}

/*
看完这个例子, 就能发现其实 QueueTestSmpl3 就是用于构建Queue对象, 并执行一系列初始化操作的
在整个测试用例执行过程中, QueueTestSmpl3一直是活跃状态, 只有等所有的测试用例执行结束, 调用TearDownTestSuite进行数据回收结束
*/
int main(int argc, char **argv) {
  // printf("Running main() from %s\n", __FILE__);
  // testing::AddGlobalTestEnvironment(new FooEnvironment);
  testing::InitGoogleTest(&argc, argv);
  return RUN_ALL_TESTS();
}

3.4 Demo4: gmock

Demo4: gmock

#include <gtest/gtest.h>
#include <gmock/gmock.h>
#include <iostream>

using namespace std;

class Cargo {
public:
    Cargo() {};
    ~Cargo() {};

    virtual bool NeedBuy(const std::string& cargoName) = 0;
    virtual bool CostValue() = 0;
    virtual int NeedCost(int value) {
        return value;
    } 
};


// 这里相当于TestCargo重新构造了一个跟Cargo一模一样的类 ,如果测试其他类时需要用到Cargo类, 就使用TestCargo类来替换即可
class TestCargo : public Cargo { 
public:
    MOCK_METHOD1(NeedCost, int(int));
    MOCK_METHOD1(NeedBuy, bool(const std::string&));
    MOCK_METHOD0(CostValue, bool());
};

TEST(TestCargo, MOCK_OK) {
    TestCargo testCargo;

    // 先制造函数的输入和指定的输出
    // 调用期待一: 调用一次后返回true, 之后调用默认返回false
    EXPECT_CALL(testCargo, CostValue()).WillOnce(testing::Return(true)); // 这里就不用管CostValue函数里面的内容具体是啥了, 直接给return即可
    // 调用期待二: 第一参数candy, 总是返回true
    EXPECT_CALL(testCargo, NeedBuy("candy")).WillRepeatedly(testing::Return(false));
    // 调用期待 三: 期待被调用5次, 后续调用返回默认值 WillOnce表示第一次调用返回true,  WillRepeatedly表示后面在调用都是返回false
    // EXPECT_CALL(testCargo, NeedBuy(_)).Times(5).WillOnce(testing::Return(true)).WillRepeatedly(testing::Return(false));
    // 调用期待四: 第一参数_, 总是返回对应的值_
    EXPECT_CALL(testCargo, NeedCost(1)).WillRepeatedly(testing::Return(1));


    // 测试
    EXPECT_TRUE(testCargo.CostValue()); // 第一次是true
    EXPECT_TRUE(testCargo.CostValue() != true); 

    EXPECT_TRUE(!testCargo.NeedBuy("candy")); // 全都是false
    EXPECT_TRUE(!testCargo.NeedBuy("candy")); // 全都是false

    EXPECT_EQ(1, testCargo.NeedCost(1));
}

/*
制造类方法的方法是 EXPECT_CALL
EXPECT_CALL(mock_object, Method(matchers))  // 调用期待, 说明对象的方法调用执行逻辑, Method是对象的mock方法, 通过match匹配
  .With(matchers)                           // 指定多个参数的匹配方法
  .Times(numbers)                           // 该方法能够被执行几次
  .InSequence(sequence)                     // 指定函数执行顺序
  .After(expectation)                       // 指定某个方法只能在另一个方法之后执行
  .WillOnce(action)                         // 执行一次方法时, 将执行参数action的方法
  .WillRepeatedly(action)                   // 一直调用方法时, 执行参数action的方法
  .RetiresOnSaturation();                   // 用于保证期待的调用不会被相同的期待覆盖

// 如: EXPECT_CALL(testCargo, NeedCost(1)).WillRepeatedly(testing::Return(1));
*/

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

结尾

本文主要是介绍了一下gtest和gmock的使用流程和细节, 有些项目中是将gtest整个源码包放到项目中,在编译过程中直接使用, 本文是将gtest直接安装到系统中, 这样直接在项目文件中写测试用例即可.

抛砖引玉, 具体高阶玩法还得看谷歌的操作手册

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

落子无悔!

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值
>