Google Test(gtest)单元测试
单元测试是一种软件测试方法,它旨在将应用程序的各个部分(通常是方法或函数)分离出来并独立测试,以确保每个部分都能够按预期工作。
gtest是Google公司发布的一款开源的C/C++单元测试框架。gtest的TEST
宏用于定义单个测试用例,其基本语法为:
TEST(TestCaseName, TestName) {
// 测试代码
}
其中 TestCaseName
为测试用例的名称,用于将相关的测试分组在一起,以便在测试结果中更容易地识别和归类。TestName
为具体测试的名称,一般描述测试的目的。
每个测试用例包含一个或多个检查点,这些检查点使用断言来验证代码的行为。包括以EXPECT_
为前缀的非致命断言,其在测试失败时程序会继续执行;和以 ASSERT_
味前缀的致命断言,其在测试失败时程序立即终止。基本的非致命断言包括:
EXPECT_EQ(val1, val2)
:检查val1 == val2
EXPECT_NE(val1, val2)
:检查val1 != val2
EXPECT_LT(val1, val2)
:检查val1 < val2
EXPECT_LE(val1, val2)
:检查val1 <= val2
EXPECT_GT(val1, val2)
:检查val1 > val2
EXPECT_GE(val1, val2)
:检查val1 >= val2
对应的致命断言:
ASSERT_EQ(val1, val2)
ASSERT_NE(val1, val2)
ASSERT_LT(val1, val2)
ASSERT_LE(val1, val2)
ASSERT_GT(val1, val2)
ASSERT_GE(val1, val2)
除此之外,还有专门用于字符串比较的断言:
EXPECT_STREQ(str1, str2)
:检查str1
和str2
是相同的字符串。EXPECT_STRNE(str1, str2)
:检查str1
和str2
是不同的字符串。EXPECT_STRCASEEQ(str1, str2)
:检查str1
和str2
是相同的字符串,忽略大小写。EXPECT_STRCASENE(str1, str2)
:检查str1
和str2
是不同的字符串,忽略大小写。
用于浮点数比较的断言:
EXPECT_FLOAT_EQ(val1, val2)
:检查val1
和val2
具有相同的浮点值。EXPECT_DOUBLE_EQ(val1, val2)
:检查val1
和val2
具有相同的双精度值。EXPECT_NEAR(val1, val2, abs_error)
:检查val1
和val2
之间的差值在abs_error
范围内。
用于布尔值的断言:
EXPECT_TRUE(condition)
:检查condition
为真。EXPECT_FALSE(condition)
:检查condition
为假。
使用示例
项目结构:
gtest_demo/
├── CMakeLists.txt
├── include/
│ └── math_functions.h
├── src/
│ └── math_functions.cpp
└── tests/
└── test_math_functions.cpp
CMakeLists.txt:
# 指定CMake的最低版本
cmake_minimum_required(VERSION 3.10)
# 定义项目名称
project(gtest_demo)
# 设置C++标准为C++11,并且为强制要求
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED True)
# 添加当前项目的include目录,以便编译器能找到头文件
include_directories(${PROJECT_SOURCE_DIR}/include)
# 添加源文件,生成一个名为math_functions的静态库
add_library(math_functions src/math_functions.cpp)
# 查找Google Test库,确保系统已安装GTest
find_package(GTest REQUIRED)
# 添加GTest的include目录
include_directories(${GTEST_INCLUDE_DIRS})
# 添加测试源文件,生成一个名为runTests的可执行文件
add_executable(runTests tests/test_math_functions.cpp)
# 链接math_functions, gtest库和pthread库到可执行文件runTests
# gtest框架在实现上使用了多线程(pthread)来管理测试并发执行
target_link_libraries(runTests ${GTEST_LIBRARIES} pthread math_functions)
# 启用测试功能
enable_testing()
# 添加一个名为runTests的测试
add_test(NAME runTests COMMAND runTests)
include/math_functions.h
#ifndef MATH_FUNCTIONS_H // 头文件保护
#define MATH_FUNCTIONS_H
int add(int a, int b);
int subtract(int a, int b);
float add(float a, float b);
double add(double a, double b);
#endif
src/math_functions.cpp :
#include "math_functions.h"
int add(int a, int b) {
return a + b + 1; // 故意错误
}
int subtract(int a, int b) {
return a - b;
}
float add(float a, float b) {
return a + b;
}
double add(double a, double b) {
return a + b;
}
tests/test_math_functions.cpp:
#include <gtest/gtest.h>
#include "math_functions.h"
// 测试add函数(整数)
TEST(MathFunctionsTest, AddInt) {
EXPECT_EQ(add(1, 1), 2);
EXPECT_EQ(add(-1, -1), -2);
EXPECT_EQ(add(0, 0),2);
}
// 测试subtract函数
TEST(MathFunctionsTest, Subtract) {
EXPECT_EQ(subtract(2, 1), 1);
EXPECT_EQ(subtract(-1, -1), 0);
EXPECT_EQ(subtract(0, 0), 0);
}
// 测试add函数(浮点数)
TEST(MathFunctionsTest, AddFloat) {
EXPECT_FLOAT_EQ(add(0.1f, 0.2f), 0.3f);
EXPECT_NEAR(add(0.1f, 0.2f), 0.3f, 1e-6);
}
TEST(MathFunctionsTest, AddDouble) {
EXPECT_DOUBLE_EQ(add(0.1, 0.2), 0.3);
EXPECT_NEAR(add(0.1, 0.2), 0.3, 1e-6);
}
int main(int argc, char **argv) {
::testing::InitGoogleTest(&argc, argv);// 初始化 Google Test
return RUN_ALL_TESTS(); // 运行所有测试用例
}
编译和运行测试
mkdir build
cd build
cmake..
make
./runTest
[==========] Running 4 tests from 1 test case.
[----------] Global test environment set-up.
[----------] 4 tests from MathFunctionsTest
[ RUN ] MathFunctionsTest.AddInt
/home/hrn/CppProjects/gtest_demo/tests/test_math_functions.cpp:6: Failure
Expected: add(1, 1)
Which is: 3
To be equal to: 2
/home/hrn/CppProjects/gtest_demo/tests/test_math_functions.cpp:7: Failure
Expected: add(-1, -1)
Which is: -1
To be equal to: -2
/home/hrn/CppProjects/gtest_demo/tests/test_math_functions.cpp:8: Failure
Expected: add(0, 0)
Which is: 1
To be equal to: 0
[ FAILED ] MathFunctionsTest.AddInt (0 ms)
[ RUN ] MathFunctionsTest.Subtract
[ OK ] MathFunctionsTest.Subtract (0 ms)
[ RUN ] MathFunctionsTest.AddFloat
[ OK ] MathFunctionsTest.AddFloat (0 ms)
[ RUN ] MathFunctionsTest.AddDouble
[ OK ] MathFunctionsTest.AddDouble (0 ms)
[----------] 4 tests from MathFunctionsTest (0 ms total)
[----------] Global test environment tear-down
[==========] 4 tests from 1 test case ran. (0 ms total)
[ PASSED ] 3 tests.
[ FAILED ] 1 test, listed below:
[ FAILED ] MathFunctionsTest.AddInt
1 FAILED TEST
3个测试通过,1个不通过,add
函数有误.
更多用法
测试夹具
测试夹具(Test Fixture)用于提供一个环境,允许开发者在多个测试用例之间共享设置和清理的代码,确保每个测试用例都在相同或可控的初始状态下运行。
在gtest中,测试夹具通常是通过派生自::testing::Test
类的子类来实现的,并通过TEST_F()
宏定义测试用例。
示例:
#include <gtest/gtest.h>
#include <vector>
// 假设有一个简单的类 MyClass
class MyClass {
public:
MyClass(int data) : basevalue(data) {}
void add(int data) { basevalue += data; }
int getdata() const { return basevalue; }
private:
int basevalue;
};
// 测试夹具类
class MyTest : public ::testing::Test {
protected:
MyClass *my;
std::vector<int> sharedVector;
// 在每个测试用例执行前设置环境
void SetUp() override {
my = new MyClass(100);
sharedVector = {1, 2, 3, 4, 5};
}
// 在每个测试用例执行后清理环境
void TearDown() override {
delete my;
}
};
// 使用 TEST_F() 宏编写测试用例
TEST_F(MyTest, test1) {
my->add(10);
EXPECT_EQ(my->getdata(), 110);
sharedVector.push_back(6);
EXPECT_EQ(sharedVector.size(), 6);
EXPECT_EQ(sharedVector.back(), 6);
}
TEST_F(MyTest, test2) {
my->add(100);
EXPECT_EQ(my->getdata(), 200);
sharedVector.pop_back();
EXPECT_EQ(sharedVector.size(), 4);
EXPECT_EQ(sharedVector.back(), 4);
}
TEST_F(MyTest, test3) {
my->add(-50);
EXPECT_EQ(my->getdata(), 50);
sharedVector[0] = 10;
EXPECT_EQ(sharedVector[0], 10);
EXPECT_EQ(sharedVector.size(), 5);
}
TEST_F(MyTest, test4) {
my->add(0);
EXPECT_EQ(my->getdata(), 100);
sharedVector.clear();
EXPECT_TRUE(sharedVector.empty());
}
在这个示例中,测试夹具类 MyTest
通过继承 ::testing::Test
类,实现了 SetUp()
和 TearDown()
方法。在 SetUp()
方法中,初始化了一个 MyClass
对象和一个 std::vector<int>
。在 TearDown()
方法中,清理了 MyClass
对象。
每个测试用例 (test1
、test2
、test3
和 test4
) 都使用了相同的测试夹具 MyTest
,共享了初始化和清理代码。在每个测试用例中,MyClass
对象和 sharedVector
都被重新初始化,以确保测试用例之间相互独立。