Google Test(简称 gtest)是 Google 开发的 C++ 单元测试框架,提供了丰富的断言宏、测试组织结构和高级功能,是 C++ 项目中最流行的测试框架之一。
核心特性
- 丰富的断言:提供多种断言宏,支持各种数据类型比较
- 测试组织:支持测试用例分组和层次化管理
- 参数化测试:支持数据驱动的测试
- 测试夹具:提供测试环境的设置和清理
- 死亡测试:测试程序崩溃或异常退出的情况
- 跨平台:支持 Windows、Linux、macOS 等平台
基本使用
1. 简单测试示例
cpp
#include <gtest/gtest.h>
// 被测试的函数
int Add(int a, int b) {
return a + b;
}
int Multiply(int a, int b) {
return a * b;
}
bool IsPrime(int n) {
if (n <= 1) return false;
if (n <= 3) return true;
if (n % 2 == 0 || n % 3 == 0) return false;
for (int i = 5; i * i <= n; i += 6) {
if (n % i == 0 || n % (i + 2) == 0)
return false;
}
return true;
}
// 基本测试用例
TEST(MathTest, Addition) {
EXPECT_EQ(Add(2, 3), 5);
EXPECT_EQ(Add(-1, 1), 0);
EXPECT_EQ(Add(0, 0), 0);
}
TEST(MathTest, Multiplication) {
EXPECT_EQ(Multiply(3, 4), 12);
EXPECT_EQ(Multiply(-2, 5), -10);
EXPECT_EQ(Multiply(0, 100), 0);
}
TEST(MathTest, PrimeCheck) {
EXPECT_TRUE(IsPrime(2));
EXPECT_TRUE(IsPrime(17));
EXPECT_FALSE(IsPrime(1));
EXPECT_FALSE(IsPrime(4));
EXPECT_FALSE(IsPrime(15));
}
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
2. 常用断言宏
cpp
#include <gtest/gtest.h>
#include <string>
#include <vector>
TEST(AssertionTest, BasicAssertions) {
// 布尔断言
EXPECT_TRUE(true);
EXPECT_FALSE(false);
// 相等性断言
EXPECT_EQ(42, 42); // 相等
EXPECT_NE(1, 2); // 不相等
EXPECT_LT(1, 2); // 小于
EXPECT_LE(1, 1); // 小于等于
EXPECT_GT(2, 1); // 大于
EXPECT_GE(2, 2); // 大于等于
}
TEST(AssertionTest, StringAssertions) {
std::string str1 = "Hello";
std::string str2 = "World";
EXPECT_STREQ("Hello", str1.c_str()); // C字符串相等
EXPECT_STRNE(str1.c_str(), str2.c_str()); // C字符串不相等
EXPECT_EQ(str1, "Hello"); // std::string相等
}
TEST(AssertionTest, FloatingPointAssertions) {
double a = 1.0;
double b = 0.9999999;
// 浮点数比较(考虑精度误差)
EXPECT_DOUBLE_EQ(1.0, 1.0);
EXPECT_NEAR(a, b, 0.001); // 在误差范围内相等
}
TEST(AssertionTest, ExceptionAssertions) {
// 异常断言
EXPECT_THROW({
throw std::runtime_error("error");
}, std::runtime_error);
EXPECT_NO_THROW({
int x = 1 + 1;
});
EXPECT_ANY_THROW({
throw std::logic_error("logic error");
});
}
3. ASSERT vs EXPECT
cpp
TEST(AssertVsExpectTest, Difference) {
// EXPECT: 失败时继续执行后续测试
EXPECT_EQ(1, 2); // 失败,但继续执行
EXPECT_EQ(3, 3); // 仍会执行
// ASSERT: 失败时立即停止当前测试
ASSERT_EQ(1, 1); // 成功,继续执行
// 如果上面的ASSERT失败,下面的代码不会执行
EXPECT_EQ(4, 4);
}
TEST(AssertVsExpectTest, WhenToUseAssert) {
int* ptr = new int(42);
// 使用ASSERT确保指针有效,避免后续空指针访问
ASSERT_NE(ptr, nullptr);
// 只有在ptr有效时才执行后续测试
EXPECT_EQ(*ptr, 42);
delete ptr;
}
测试夹具 (Test Fixtures)
测试夹具用于为多个测试提供共同的设置和清理代码:
cpp
#include <gtest/gtest.h>
#include <vector>
#include <memory>
// 被测试的类
class Calculator {
public:
void Add(int value) { result_ += value; }
void Subtract(int value) { result_ -= value; }
void Multiply(int value) { result_ *= value; }
void Divide(int value) {
if (value == 0) throw std::invalid_argument("Division by zero");
result_ /= value;
}
void Clear() { result_ = 0; }
int GetResult() const { return result_; }
private:
int result_ = 0;
};
// 测试夹具类
class CalculatorTest : public ::testing::Test {
protected:
// 每个测试前调用
void SetUp() override {
calc_ = std::make_unique<Calculator>();
calc_->Clear();
}
// 每个测试后调用
void TearDown() override {
calc_.reset();
}
// 测试数据
std::unique_ptr<Calculator> calc_;
};
// 使用测试夹具的测试
TEST_F(CalculatorTest, Addition) {
calc_->Add(5);
calc_->Add(3);
EXPECT_EQ(calc_->GetResult(), 8);
}
TEST_F(CalculatorTest, Subtraction) {
calc_->Add(10);
calc_->Subtract(3);
EXPECT_EQ(calc_->GetResult(), 7);
}
TEST_F(CalculatorTest, Multiplication) {
calc_->Add(4);
calc_->Multiply(3);
EXPECT_EQ(calc_->GetResult(), 12);
}
TEST_F(CalculatorTest, Division) {
calc_->Add(15);
calc_->Divide(3);
EXPECT_EQ(calc_->GetResult(), 5);
}
TEST_F(CalculatorTest, DivisionByZero) {
calc_->Add(10);
EXPECT_THROW(calc_->Divide(0), std::invalid_argument);
}
参数化测试
参数化测试允许使用不同的输入数据运行相同的测试逻辑:
cpp
#include <gtest/gtest.h>
// 被测试的函数
bool IsEven(int n) {
return n % 2 == 0;
}
int Factorial(int n) {
if (n <= 1) return 1;
return n * Factorial(n - 1);
}
// 参数化测试类
class EvenNumberTest : public ::testing::TestWithParam<int> {
};
// 参数化测试用例
TEST_P(EvenNumberTest, CheckEvenNumbers) {
int number = GetParam();
EXPECT_TRUE(IsEven(number));
}
// 实例化参数化测试
INSTANTIATE_TEST_SUITE_P(
EvenNumbers,
EvenNumberTest,
::testing::Values(0, 2, 4, 6, 8, 10, -2, -4)
);
// 更复杂的参数化测试
struct FactorialTestData {
int input;
int expected;
};
class FactorialTest : public ::testing::TestWithParam<FactorialTestData> {
};
TEST_P(FactorialTest, ComputeFactorial) {
auto data = GetParam();
EXPECT_EQ(Factorial(data.input), data.expected);
}
INSTANTIATE_TEST_SUITE_P(
FactorialTestCases,
FactorialTest,
::testing::Values(
FactorialTestData{0, 1},
FactorialTestData{1, 1},
FactorialTestData{2, 2},
FactorialTestData{3, 6},
FactorialTestData{4, 24},
FactorialTestData{5, 120}
)
);
// 使用范围生成参数
class RangeTest : public ::testing::TestWithParam<int> {
};
TEST_P(RangeTest, SquareIsPositive) {
int n = GetParam();
EXPECT_GE(n * n, 0);
}
INSTANTIATE_TEST_SUITE_P(
NumberRange,
RangeTest,
::testing::Range(-5, 6) // -5 到 5
);
死亡测试
死亡测试用于验证程序在特定条件下会崩溃或异常退出:
cpp
#include <gtest/gtest.h>
#include <cassert>
void CrashFunction() {
abort();
}
void AssertFunction(bool condition) {
assert(condition);
}
void ExitFunction(int code) {
exit(code);
}
TEST(DeathTest, CrashTest) {
EXPECT_DEATH(CrashFunction(), "");
}
TEST(DeathTest, AssertTest) {
EXPECT_DEATH(AssertFunction(false), "Assertion.*failed");
}
TEST(DeathTest, ExitTest) {
EXPECT_EXIT(ExitFunction(1), ::testing::ExitedWithCode(1), "");
EXPECT_EXIT(ExitFunction(0), ::testing::ExitedWithCode(0), "");
}
// 更安全的死亡测试(在子进程中运行)
TEST(DeathTest, SafeDeathTest) {
::testing::FLAGS_gtest_death_test_style = "threadsafe";
EXPECT_DEATH({
int* p = nullptr;
*p = 42; // 空指针解引用
}, "");
}
自定义断言和匹配器
cpp
#include <gtest/gtest.h>
#include <gmock/gmock.h>
// 自定义断言函数
::testing::AssertionResult IsEvenNumber(int n) {
if (n % 2 == 0) {
return ::testing::AssertionSuccess();
} else {
return ::testing::AssertionFailure() << n << " is not even";
}
}
// 自定义匹配器
MATCHER_P(IsDivisibleBy, divisor, "") {
return (arg % divisor) == 0;
}
MATCHER(IsPositive, "") {
return arg > 0;
}
TEST(CustomAssertionTest, EvenNumberTest) {
EXPECT_TRUE(IsEvenNumber(4));
EXPECT_FALSE(IsEvenNumber(5));
}
TEST(CustomMatcherTest, DivisibilityTest) {
EXPECT_THAT(15, IsDivisibleBy(3));
EXPECT_THAT(15, IsDivisibleBy(5));
EXPECT_THAT(15, ::testing::Not(IsDivisibleBy(4)));
}
TEST(CustomMatcherTest, PositiveTest) {
EXPECT_THAT(42, IsPositive());
EXPECT_THAT(-5, ::testing::Not(IsPositive()));
}
// 容器匹配器
TEST(ContainerMatcherTest, VectorTest) {
std::vector<int> numbers = {1, 2, 3, 4, 5};
EXPECT_THAT(numbers, ::testing::Contains(3));
EXPECT_THAT(numbers, ::testing::Not(::testing::Contains(6)));
EXPECT_THAT(numbers, ::testing::ElementsAre(1, 2, 3, 4, 5));
EXPECT_THAT(numbers, ::testing::SizeIs(5));
}
测试套件的设置和清理
cpp
#include <gtest/gtest.h>
#include <fstream>
// 全局设置和清理
class GlobalEnvironment : public ::testing::Environment {
public:
void SetUp() override {
// 全局初始化
std::cout << "Global setup\n";
// 创建测试文件
std::ofstream file("test_data.txt");
file << "test data";
file.close();
}
void TearDown() override {
// 全局清理
std::cout << "Global teardown\n";
// 删除测试文件
std::remove("test_data.txt");
}
};
// 测试套件级别的设置和清理
class FileTest : public ::testing::Test {
public:
// 整个测试套件开始前调用一次
static void SetUpTestSuite() {
std::cout << "FileTest suite setup\n";
}
// 整个测试套件结束后调用一次
static void TearDownTestSuite() {
std::cout << "FileTest suite teardown\n";
}
protected:
// 每个测试前调用
void SetUp() override {
std::cout << "Individual test setup\n";
}
// 每个测试后调用
void TearDown() override {
std::cout << "Individual test teardown\n";
}
};
TEST_F(FileTest, ReadFile) {
std::ifstream file("test_data.txt");
ASSERT_TRUE(file.is_open());
std::string content;
std::getline(file, content);
EXPECT_EQ(content, "test data");
}
TEST_F(FileTest, FileExists) {
std::ifstream file("test_data.txt");
EXPECT_TRUE(file.good());
}
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
// 注册全局环境
::testing::AddGlobalTestEnvironment(new GlobalEnvironment);
return RUN_ALL_TESTS();
}
测试过滤和运行选项
bash
# 运行所有测试
./test_executable
# 运行特定测试套件
./test_executable --gtest_filter=MathTest.*
# 运行特定测试用例
./test_executable --gtest_filter=MathTest.Addition
# 排除特定测试
./test_executable --gtest_filter=-MathTest.Multiplication
# 重复运行测试
./test_executable --gtest_repeat=5
# 随机顺序运行测试
./test_executable --gtest_shuffle
# 输出详细信息
./test_executable --gtest_verbose
# 生成XML报告
./test_executable --gtest_output=xml:test_results.xml
CMake 集成示例
cmake
# CMakeLists.txt
cmake_minimum_required(VERSION 3.14)
project(MyProject)
# 设置C++标准
set(CMAKE_CXX_STANDARD 17)
# 查找或下载Google Test
include(FetchContent)
FetchContent_Declare(
googletest
GIT_REPOSITORY https://github.com/google/googletest.git
GIT_TAG v1.14.0 # 使用最新稳定版本
)
FetchContent_MakeAvailable(googletest)
# 添加可执行文件
add_executable(
my_tests
test_main.cpp
math_test.cpp
calculator_test.cpp
)
# 链接Google Test
target_link_libraries(
my_tests
gtest_main
gmock_main
)
# 启用测试
enable_testing()
# 添加测试
include(GoogleTest)
gtest_discover_tests(my_tests)
最佳实践
1. 测试命名规范
cpp
// 好的命名:描述性强,清晰表达测试意图
TEST(StringUtilsTest, SplitString_WithCommaDelimiter_ReturnsCorrectParts)
TEST(UserManagerTest, CreateUser_WithValidData_ReturnsSuccess)
TEST(DatabaseTest, Connect_WithInvalidCredentials_ThrowsException)
// 避免的命名:过于简单,不够描述性
TEST(Test1, Case1)
TEST(MyTest, TestFunction)
2. 测试组织
cpp
// 按功能模块组织测试
namespace math_utils {
TEST(AdditionTest, PositiveNumbers) { /* ... */ }
TEST(AdditionTest, NegativeNumbers) { /* ... */ }
TEST(SubtractionTest, BasicCases) { /* ... */ }
}
namespace string_utils {
TEST(SplitTest, EmptyString) { /* ... */ }
TEST(SplitTest, SingleDelimiter) { /* ... */ }
TEST(TrimTest, WhitespaceOnly) { /* ... */ }
}
3. 测试数据管理
cpp
// 使用常量定义测试数据
namespace test_data {
const std::vector<int> VALID_NUMBERS = {1, 2, 3, 4, 5};
const std::string SAMPLE_TEXT = "Hello, World!";
const double TOLERANCE = 1e-6;
}
TEST(DataTest, UseTestConstants) {
EXPECT_THAT(test_data::VALID_NUMBERS, ::testing::SizeIs(5));
EXPECT_NEAR(CalculateAverage(test_data::VALID_NUMBERS), 3.0, test_data::TOLERANCE);
}
总结
Google Test 是一个功能强大且易用的 C++ 测试框架,提供了:
- 丰富的断言宏:支持各种数据类型和条件的验证
- 灵活的测试组织:测试用例、测试夹具、测试套件
- 高级功能:参数化测试、死亡测试、自定义匹配器
- 良好的集成:支持 CMake、CI/CD 等开发工具链
- 详细的报告:提供清晰的测试结果和失败信息
通过合理使用 Google Test,可以显著提高代码质量,确保软件的可靠性和稳定性。建议在项目开发过程中采用测试驱动开发(TDD)的方式,先写测试再实现功能,这样可以更好地设计 API 并确保代码的正确性。