探索 Google Test: 从基础断言到高级 Mock 技巧
文章目录
- 基本用法
- 测试整型或布尔值
- 比较字符串
- 比较浮点数
- 高级用法
- 测试异常
- 测试退出
- 模拟 (Mock) 代码
- 总结
- 源码链接
Google Test 是一个广泛使用的 C++ 单元测试框架, 它提供了强大的功能, 友好的断言语法和易于集成的特性. 以下是如何使用 Google Test 测试 C++ 代码的详细介绍:
基本用法
Google Test 提供了一系列断言方法, 用于测试不同类型的值. 以下是一些常见用法:
测试整型或布尔值
#include <gtest/gtest.h>
int Add(int a, int b) { return a + b; }
int Subtract(int a, int b) { return a - b; }
TEST(BasicUsageTest, TestIntegerAndBoolean) {
EXPECT_EQ(Add(2, 3), 5); // 测试是否等于
EXPECT_NE(Add(2, 2), 5); // 测试是否不等于
EXPECT_GT(Subtract(10, 5), 3); // 测试是否大于
EXPECT_LT(Subtract(5, 10), 0); // 测试是否小于
ASSERT_TRUE(Add(1, 1) == 2); // 测试布尔表达式为真
ASSERT_FALSE(Add(1, 1) == 3); // 测试布尔表达式为假
}
比较字符串
// string compare
std::string GetGreeting() { return "Hello, World!"; }
TEST(BasicUsageTest, TestStringComparison) {
EXPECT_STREQ(GetGreeting().c_str(), "Hello, World!"); // 测试字符串是否相等
EXPECT_STRNE(GetGreeting().c_str(), "Hi, World!"); // 测试字符串是否不等
}
比较浮点数
比较浮点数时不能直接使用 EXPECT_EQ
, 因为有如下的限制:
- 舍入误差: 浮点数运算可能会引入舍入误差, 例如,
1.0 / 3.0
的结果在双精度下表示为0.3333333333333333
, 但期望值可能是0.33333333
.
直接用EXPECT_EQ
会因为微小的误差导致测试失败. - 环境依赖性: 不同的编译器, 硬件架构或优化级别可能会影响浮点运算结果的精度.
- 浮点精度限制:浮点数有固定的有效位数, 因此无法精确表示某些小数, 例如 0.1 在二进制中是一个无限循环的值.
// float point compare
double Divide(double a, double b) { return a / b; }
TEST(BasicUsageTest, TestFloatingPointComparison) {
EXPECT_FLOAT_EQ(Divide(1.0, 3.0), 0.33333333f); // 测试浮点数是否相等
EXPECT_NEAR(Divide(10.0, 3.0), 3.333, 0.001); // 测试浮点数是否接近
}
高级用法
测试异常
测试代码是否抛出异常以及异常的内容:
#include <gtest/gtest.h>
#include <stdexcept>
void ThrowIfNegative(int value) {
if (value < 0) {
throw std::invalid_argument("Negative value not allowed");
}
}
TEST(BasicUsageTest, TestExceptions) {
// 测试是否抛出指定类型的异常
EXPECT_THROW(ThrowIfNegative(-1), std::invalid_argument);
// 测试是否抛出任意异常
EXPECT_ANY_THROW(ThrowIfNegative(-2));
// 测试是否不抛出异常
EXPECT_NO_THROW(ThrowIfNegative(1));
}
测试退出
测试代码是否调用 exit
终止:
#include <gtest/gtest.h>
#include <cstdlib>
// 遇到0则进程退出
void TerminateIfZero(int value) {
if (value == 0) {
std::exit(EXIT_FAILURE);
}
}
TEST(BasicUsageTest, TestExit) {
// 测试是否退出并匹配退出码
EXPECT_EXIT(TerminateIfZero(0), ::testing::ExitedWithCode(EXIT_FAILURE), "");
// 测试正常情况是否不退出
EXPECT_NO_THROW(TerminateIfZero(1));
}
模拟 (Mock) 代码
假设有一个纯虚接口类, 用于定义一个简单的数据库操作:
#pragma once
#include <string>
class DBInterface {
public:
virtual bool Connect(const std::string& url) = 0;
virtual std::string Query(const std::string& query) = 0;
// 必须要有一个virtual的析构函数
virtual ~DBInterface() = default;
};
同时, 在应用中这样使用该接口:
#pragma once
#include "interface.h"
class App {
public:
explicit App(DBInterface* link) : db_(link) {}
void Setup(const std::string& url) {
if (!db_->Connect(url)) {
return;
}
auto config = db_->Query("select config from config_table");
// ... 其他处理
}
private:
DBInterface* db_ = nullptr;
};
可以使用 Google Mock 模拟这个接口:
#include <gmock/gmock.h>
class MockDatabase : public Database {
public:
MOCK_METHOD(bool, Connect, (const std::string& url), (override));
MOCK_METHOD(std::string, Query, (const std::string& query), (override));
};
在测试的时候则可以传进去我们的Mock
类实现.
TEST(MockTest, TestConnectFail) {
MockDatabase db;
// 设置返回值为false, 用来模拟调用失败
EXPECT_CALL(db, Connect("test_url"))
.Times(1)
.WillOnce(::testing::Return(false));
App app(&db);
app.Setup("test_url");
}
TEST(MockTest, TestConnectSuccess) {
MockDatabase db;
// 设置返回值为true, 模拟调用成功
EXPECT_CALL(db, Connect("success_url"))
.Times(1)
.WillOnce(::testing::Return(true));
// APP中的逻辑, 在Connect成功之后会进行Query
EXPECT_CALL(db, Query("select config from config_table"))
.Times(1)
.WillOnce(::testing::Return("mocked result"));
App app(&db);
app.Setup("success_url");
}
可以看到我们分别模拟了成功/失败的情况, 这种时候我们不需要真实的数据库, 这样一方面不方便另一方面也不稳定, 不能控制其返回值.
总结
Google Test 是一个功能强大且灵活的 C++ 测试框架, 适合从基础的断言测试到高级的 Mock 行为模拟. 通过合理使用 Google Test 的功能, 开发者可以轻松构建健壮的单元测试体系, 确保代码质量, 并快速定位和修复潜在问题.
源码链接
源码链接