C++ 测试框架
为什么需要测试框架?
在编写C++程序时,确保代码的正确性和稳定性至关重要。手动测试每个功能不仅繁琐,还容易出错。这时,测试框架就成了开发者的得力助手。
测试框架的价值
测试框架帮助开发者自动化测试过程,提高测试效率,发现潜在问题,并确保代码修改不会破坏现有功能。
C++ 常用测试框架概览
在C++生态系统中,有几个流行的测试框架:
- Google Test (gtest) - 谷歌开发的全功能C++测试框架
- Catch2 - 轻量级的、现代化的C++测试框架
- Boost.Test - Boost库提供的测试工具
- doctest - 极轻量级的单头文件测试框架
- CppUnit - C++版的JUnit
本文将主要介绍Google Test和Catch2,它们是目前使用最广泛的两个框架。
Google Test入门
安装与配置
首先,我们需要安装Google Test。以下是基本步骤:
- 从GitHub下载源码
- 构建库:
mkdir build && cd build
cmake ..
make - 链接到你的项目
编写第一个测试
下面是一个使用Google Test的简单示例:
#include <gtest/gtest.h>
// 被测试的函数
int Add(int a, int b) {
return a + b;
}
// 测试用例
TEST(AdditionTest, PositiveNumbers) {
EXPECT_EQ(5, Add(2, 3));
EXPECT_EQ(10, Add(4, 6));
}
TEST(AdditionTest, NegativeNumbers) {
EXPECT_EQ(-5, Add(-2, -3));
EXPECT_EQ(-1, Add(-4, 3));
}
int main(int argc, char **argv) {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
运行测试
编译并运行上述代码,你会看到如下输出:
[==========] Running 2 tests from 1 test suite.
[----------] Global test environment set-up.
[----------] 2 tests from AdditionTest
[ RUN ] AdditionTest.PositiveNumbers
[ OK ] AdditionTest.PositiveNumbers (0 ms)
[ RUN ] AdditionTest.NegativeNumbers
[ OK ] AdditionTest.NegativeNumbers (0 ms)
[----------] 2 tests from AdditionTest (0 ms total)
[----------] Global test environment tear-down
[==========] 2 tests from 1 test suite ran. (0 ms total)
[ PASSED ] 2 tests.
断言与验证
Google Test提供两类检查机制:
- ASSERT_*:断言失败时停止当前测试
- EXPECT_*:验证失败时继续执行,但标记测试失败
常用断言:
ASSERT_EQ(expected, actual)
/EXPECT_EQ(expected, actual)
:检查相等ASSERT_NE(val1, val2)
/EXPECT_NE(val1, val2)
:检查不相等ASSERT_LT(val1, val2)
/EXPECT_LT(val1, val2)
:检查小于ASSERT_GT(val1, val2)
/EXPECT_GT(val1, val2)
:检查大于ASSERT_TRUE(condition)
/EXPECT_TRUE(condition)
:检查条件为真
Catch2框架
Catch2是一个更现代、更简洁的C++测试框架,它的特点是只需要包含一个头文件。
安装与配置
使用Catch2非常简单:
- 下载
catch.hpp
文件(或使用包管理器) - 在测试文件中包含这个头文件
编写Catch2测试
#define CATCH_CONFIG_MAIN // 这将让Catch2自动生成main()函数
#include <catch2/catch.hpp>
unsigned int Factorial(unsigned int number) {
return number <= 1 ? 1 : number * Factorial(number - 1);
}
TEST_CASE("Factorials are computed", "[factorial]") {
REQUIRE(Factorial(0) == 1);
REQUIRE(Factorial(1) == 1);
REQUIRE(Factorial(2) == 2);
REQUIRE(Factorial(3) == 6);
REQUIRE(Factorial(10) == 3628800);
}
TEST_CASE("Factorial edge cases", "[factorial]") {
SECTION("Zero factorial") {
REQUIRE(Factorial(0) == 1);
}
SECTION("Large factorials") {
CHECK(Factorial(10) == 3628800);
}
}
Catch2特性
Catch2的一些亮点:
- 只需包含一个头文件
- BDD风格的SECTION支持
- 自动REQUIRE和CHECK断言
- 详细的测试报告
- 无需宏定义测试名称
测试驱动开发 (TDD) 与测试框架
测试驱动开发是一种软件开发方法,它遵循以下循环:
- 编写测试
- 运行测试(此时应该失败)
- 编写最小代码使测试通过
- 重构代码
- 重复
让我们用一个简单的例子来实践TDD: