GoogleTest — 单元测试框架
GoogleTest(gtest)是 Google 开源的 C++ 测试框架,功能完整,与 CMake 集成良好,是 C++ 项目的测试标准。
安装与配置
cmake
# CMakeLists.txt
include(FetchContent)
FetchContent_Declare(
googletest
GIT_REPOSITORY https://github.com/google/googletest.git
GIT_TAG v1.14.0
)
FetchContent_MakeAvailable(googletest)
enable_testing()
add_executable(my_tests
test_math.cpp
test_string.cpp
)
target_link_libraries(my_tests PRIVATE GTest::gtest_main GTest::gmock_main)
include(GoogleTest)
gtest_discover_tests(my_tests)基本断言
cpp
#include <gtest/gtest.h>
// 测试用例:TEST(测试套件名, 测试名)
TEST(MathTest, Addition) {
EXPECT_EQ(2 + 2, 4); // 相等(失败继续)
ASSERT_EQ(2 + 2, 4); // 相等(失败停止当前测试)
EXPECT_NE(1, 2); // 不相等
EXPECT_LT(1, 2); // 小于
EXPECT_LE(1, 2); // 小于等于
EXPECT_GT(2, 1); // 大于
EXPECT_GE(2, 1); // 大于等于
EXPECT_TRUE(1 + 1 == 2);
EXPECT_FALSE(1 + 1 == 3);
}
TEST(StringTest, Operations) {
std::string s = "hello";
EXPECT_EQ(s.size(), 5u);
EXPECT_STREQ("hello", s.c_str()); // C 字符串比较
EXPECT_STRNE("hello", "world");
// 浮点比较(避免精度问题)
EXPECT_FLOAT_EQ(3.14f, 3.14f);
EXPECT_DOUBLE_EQ(3.14, 3.14);
EXPECT_NEAR(3.14, 3.141, 0.01); // 误差范围内
}测试夹具(Test Fixture)
cpp
#include <gtest/gtest.h>
class DatabaseTest : public ::testing::Test {
protected:
void SetUp() override {
// 每个测试前执行
db_.connect("test_db");
db_.execute("CREATE TABLE users (id INT, name TEXT)");
}
void TearDown() override {
// 每个测试后执行
db_.execute("DROP TABLE users");
db_.disconnect();
}
Database db_; // 共享的测试资源
};
TEST_F(DatabaseTest, InsertUser) {
db_.execute("INSERT INTO users VALUES (1, 'Alice')");
auto result = db_.query("SELECT * FROM users");
EXPECT_EQ(result.size(), 1u);
EXPECT_EQ(result[0]["name"], "Alice");
}
TEST_F(DatabaseTest, DeleteUser) {
db_.execute("INSERT INTO users VALUES (1, 'Alice')");
db_.execute("DELETE FROM users WHERE id=1");
auto result = db_.query("SELECT * FROM users");
EXPECT_TRUE(result.empty());
}
// 套件级别的 SetUp/TearDown(整个套件只执行一次)
class ExpensiveTest : public ::testing::Test {
protected:
static void SetUpTestSuite() {
// 整个测试套件开始前执行一次
shared_resource_ = new ExpensiveResource();
}
static void TearDownTestSuite() {
delete shared_resource_;
}
static ExpensiveResource* shared_resource_;
};参数化测试
cpp
#include <gtest/gtest.h>
// 参数化测试:用不同参数运行同一测试
class PrimeTest : public ::testing::TestWithParam<int> {};
TEST_P(PrimeTest, IsPrime) {
int n = GetParam();
EXPECT_TRUE(is_prime(n));
}
INSTANTIATE_TEST_SUITE_P(
PrimeNumbers,
PrimeTest,
::testing::Values(2, 3, 5, 7, 11, 13, 17, 19, 23)
);
// 多参数组合
class AddTest : public ::testing::TestWithParam<std::tuple<int, int, int>> {};
TEST_P(AddTest, Sum) {
auto [a, b, expected] = GetParam();
EXPECT_EQ(a + b, expected);
}
INSTANTIATE_TEST_SUITE_P(
AddCases,
AddTest,
::testing::Values(
std::make_tuple(1, 2, 3),
std::make_tuple(0, 0, 0),
std::make_tuple(-1, 1, 0)
)
);异常测试
cpp
TEST(ExceptionTest, ThrowsOnInvalidInput) {
// 期望抛出特定异常
EXPECT_THROW(parse_int("abc"), std::invalid_argument);
// 期望抛出任何异常
EXPECT_ANY_THROW(risky_operation());
// 期望不抛出异常
EXPECT_NO_THROW(safe_operation());
// 捕获并检查异常内容
try {
parse_int("abc");
FAIL() << "应该抛出异常";
} catch (const std::invalid_argument& e) {
EXPECT_STREQ(e.what(), "invalid integer");
}
}Google Mock
cpp
#include <gmock/gmock.h>
// 定义接口
class Database {
public:
virtual ~Database() = default;
virtual bool connect(const std::string& url) = 0;
virtual std::vector<Row> query(const std::string& sql) = 0;
virtual bool execute(const std::string& sql) = 0;
};
// 创建 Mock
class MockDatabase : public Database {
public:
MOCK_METHOD(bool, connect, (const std::string& url), (override));
MOCK_METHOD(std::vector<Row>, query, (const std::string& sql), (override));
MOCK_METHOD(bool, execute, (const std::string& sql), (override));
};
TEST(UserServiceTest, CreateUser) {
MockDatabase mock_db;
// 设置期望
EXPECT_CALL(mock_db, connect("test_db"))
.Times(1)
.WillOnce(::testing::Return(true));
EXPECT_CALL(mock_db, execute(::testing::HasSubstr("INSERT")))
.Times(1)
.WillOnce(::testing::Return(true));
UserService service(&mock_db);
service.connect("test_db");
bool ok = service.create_user("Alice", 30);
EXPECT_TRUE(ok);
// 测试结束时自动验证所有期望
}运行测试
bash
# 运行所有测试
./my_tests
# 过滤测试
./my_tests --gtest_filter="MathTest.*"
./my_tests --gtest_filter="*Addition*"
./my_tests --gtest_filter="-*Slow*" # 排除
# 重复运行(检测偶发失败)
./my_tests --gtest_repeat=100
# 输出 XML 报告
./my_tests --gtest_output=xml:report.xml
# CMake 运行
ctest --output-on-failure
ctest -R "MathTest" --verbose关键认知
测试夹具(TEST_F)是组织测试的核心,SetUp/TearDown 保证每个测试的独立性。参数化测试避免重复代码。Google Mock 让依赖注入和接口测试变得简单。始终用 EXPECT_* 而非 ASSERT_*,除非后续测试依赖当前断言。