文章目录
- 1. 介绍
- 2. 源码编译安装
- 3. demo
- 3.1. Demo1: 简单的利用gtest给定的一些宏进行测试
- 3.2 gtest给的一些宏
- 3.3 利用gtest测试方法和类
- a. Demo2: TEST
- b. Demo3: TEST_F
- 3.4 Demo4: gmock
- 结尾
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 一元比较
ASSERT | EXPECT | Verifies |
| | |
| | |
表2 二元比较
ASSERT | EXPECT | Condition |
| | |
| | |
| | |
| | |
| | |
| | |
字符串检查
Fatal assertion | Nonfatal assertion | Verifies |
| | |
| | |
| | |
| | |
异常检查
Fatal assertion | Nonfatal assertion | Verifies |
| | |
| | |
| | |
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直接安装到系统中, 这样直接在项目文件中写测试用例即可.
抛砖引玉, 具体高阶玩法还得看谷歌的操作手册