C++ 测试与调试实战:保障代码质量与稳定性
学习目标
掌握 C++ 测试与调试的核心技能,确保交付的代码具备高质量和稳定性。通过本章学习,你将能够:
- 理解测试与调试的基本概念及常用方法
- 熟练使用 Google Test 和 Catch2 等单元测试框架
- 认识集成测试的重要性并实践系统功能验证
- 掌握 GDB 和 Visual Studio 等调试工具的使用技巧
- 建立测试驱动的开发思维,编写更健壮的代码
测试基础概览
测试分类
在 C++ 开发中,测试通常分为以下几类:
- 单元测试:针对单个函数或类的功能进行验证
- 集成测试:验证多个模块协同工作的正确性
- 系统测试:对整个系统的功能进行全面测试
- 验收测试:确认系统是否满足用户需求
- 性能测试:评估系统的性能指标
核心原则
有效的测试应遵循以下原则:
- 尽早介入测试流程
- 覆盖尽可能多的场景
- 实现自动化执行
- 保证结果可重复
- 保持测试独立性
主流单元测试框架
Google Test (gtest)
Google Test 是业界广泛使用的 C++ 单元测试框架,提供丰富的断言宏。
安装指南
# Ubuntu/Debian
sudo apt-get install libgtest-dev
# macOS (Homebrew)
brew install googletest
代码示例
下面是一个使用 Google Test 测试 MyClass 的完整示例:
#include <gtest/gtest.h>
#include "MyClass.h"
// 测试构造函数
TEST(MyClassTest, ConstructorTest) {
MyClass obj;
EXPECT_EQ(obj.getValue(), 0);
}
// 测试 setValue 和 getValue 方法
TEST(MyClassTest, SetGetValueTest) {
MyClass obj;
obj.setValue(42);
EXPECT_EQ(obj.getValue(), 42);
}
// 测试 add 方法
TEST(MyClassTest, AddTest) {
MyClass obj;
obj.setValue(10);
int result = obj.add(20);
EXPECT_EQ(result, 30);
EXPECT_EQ(obj.getValue(), 30);
}
// 测试 subtract 方法
TEST(MyClassTest, SubtractTest) {
MyClass obj;
obj.setValue(50);
int result = obj.subtract(20);
EXPECT_EQ(result, 30);
EXPECT_EQ(obj.getValue(), 30);
}
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
Catch2 框架
Catch2 提供了更简洁的语法和丰富的功能,适合快速编写测试用例。
安装指南
# Ubuntu/Debian
sudo apt-get install catch2
# macOS (Homebrew)
brew install catch2
代码示例
#define CATCH_CONFIG_MAIN
#include <catch2/catch.hpp>
#include "MyClass.h"
TEST_CASE("MyClass Constructor Test", "[constructor]") {
MyClass obj;
REQUIRE(obj.getValue() == 0);
}
TEST_CASE("MyClass Set and Get Value Test", "[setget]") {
MyClass obj;
obj.setValue(42);
REQUIRE(obj.getValue() == 42);
}
TEST_CASE("MyClass Add Test", "[add]") {
MyClass obj;
obj.setValue(10);
int result = obj.add(20);
REQUIRE(result == 30);
REQUIRE(obj.getValue() == 30);
}
TEST_CASE("MyClass Subtract Test", "[subtract]") {
MyClass obj;
obj.setValue(50);
int result = obj.subtract(20);
REQUIRE(result == 30);
REQUIRE(obj.getValue() == 30);
}
调试工具实战
GDB 调试器
GDB 是 Linux 环境下最强大的命令行调试器。
基本命令
编译时务必添加 -g 参数以包含调试信息:
g++ -g program.cpp -o program
gdb program
常用命令速查:
break main:在 main 函数处设置断点run:运行程序next:执行下一行(不进入函数)step:执行下一行(进入函数)print variable:打印变量值watch variable:监听变量变化backtrace:查看调用栈continue:继续执行quit:退出调试
调试示例
#include <iostream>
#include "MyClass.h"
int main() {
MyClass obj;
obj.setValue(42);
int result = obj.add(20);
std::cout << "结果:" << result << std::endl;
return 0;
}
Visual Studio 调试器
Windows 下推荐使用 Visual Studio,其图形化界面直观易用。
操作步骤:
- 在代码行号旁点击设置断点
- 按 F5 启动调试
- 程序暂停后,观察变量窗口和调用堆栈
- 使用单步调试逐步排查逻辑
集成测试策略
基本概念
集成测试关注模块间的交互,常见策略包括:
- 自顶向下:从顶层开始逐步集成底层
- 自底向上:从底层开始逐步集成顶层
- 三明治集成:结合上述两种方法
示例代码
#include <gtest/gtest.h>
#include "Calculator.h"
#include "Parser.h"
TEST(IntegrationTest, CalculateTest) {
Calculator calculator;
Parser parser;
// 测试加法
std::string expression = "10 + 20";
int result = parser.parse(expression);
EXPECT_EQ(result, 30);
// 测试乘法
expression = "10 * 20";
result = parser.parse(expression);
EXPECT_EQ(result, 200);
// 测试括号优先级
expression = "(10 + 20) * 3";
result = parser.parse(expression);
EXPECT_EQ(result, 90);
}
综合案例:计算器项目实战
项目结构
CalculatorProject/
├── include/
│ ├── Calculator.h
│ └── Parser.h
├── src/
│ ├── Calculator.cpp
│ ├── Parser.cpp
│ └── main.cpp
├── tests/
│ ├── CalculatorTest.cpp
│ ├── ParserTest.cpp
│ └── IntegrationTest.cpp
└── CMakeLists.txt
核心实现
Calculator.h
#ifndef CALCULATOR_H
#define CALCULATOR_H
class Calculator {
public:
static int add(int a, int b);
static int subtract(int a, int b);
static int multiply(int a, int b);
static int divide(int a, int b);
};
#endif
Calculator.cpp
#include "Calculator.h"
#include <stdexcept>
int Calculator::add(int a, int b) {
return a + b;
}
int Calculator::subtract(int a, int b) {
return a - b;
}
int Calculator::multiply(int a, int b) {
return a * b;
}
int Calculator::divide(int a, int b) {
if (b == 0) {
throw std::invalid_argument("除数不能为零");
}
return a / b;
}
Parser.h
#ifndef PARSER_H
#define PARSER_H
#include <string>
#include "Calculator.h"
class Parser {
public:
int parse(const std::string& expression);
};
#endif
Parser.cpp
#include "Parser.h"
#include <sstream>
int Parser::parse(const std::string& expression) {
std::istringstream iss(expression);
int a, b;
char op;
iss >> a >> op >> b;
switch (op) {
case '+': return Calculator::add(a, b);
case '-': return Calculator::subtract(a, b);
case '*': return Calculator::multiply(a, b);
case '/': return Calculator::divide(a, b);
default: throw std::invalid_argument("无效的运算符");
}
}
main.cpp
#include <iostream>
#include "Parser.h"
int main() {
Parser parser;
try {
std::string expression;
std::cout << "请输入表达式(例如:10 + 20):";
std::getline(std::cin, expression);
int result = parser.parse(expression);
std::cout << "结果:" << result << std::endl;
} catch (const std::exception& e) {
std::cerr << "错误:" << e.what() << std::endl;
return 1;
}
return 0;
}
构建与测试
使用 CMake 管理项目构建:
CMakeLists.txt
cmake_minimum_required(VERSION 3.10)
project(CalculatorProject)
set(CMAKE_CXX_STANDARD 17)
include_directories(include)
add_library(Calculator src/Calculator.cpp)
add_library(Parser src/Parser.cpp)
add_executable(CalculatorApp src/main.cpp)
target_link_libraries(CalculatorApp Calculator Parser)
add_subdirectory(tests)
tests/CMakeLists.txt
cmake_minimum_required(VERSION 3.10)
find_package(GTest REQUIRED)
include_directories(${GTEST_INCLUDE_DIRS})
add_executable(CalculatorTests
CalculatorTest.cpp
ParserTest.cpp
IntegrationTest.cpp
)
target_link_libraries(CalculatorTests ${GTEST_LIBRARIES} Calculator Parser pthread)
include(GoogleTest)
gtest_discover_tests(CalculatorTests)
编译运行
mkdir -p build && cd build
cmake ..
make
ctest
总结与进阶
通过本章内容,我们梳理了 C++ 测试与调试的关键技术点:
- 掌握了测试分类与核心原则
- 熟悉了 Google Test 和 Catch2 的用法
- 学会了使用 GDB 和 VS 进行调试
- 理解了集成测试的价值
- 完成了计算器项目的完整测试闭环
后续练习建议:
- 尝试用 Google Test 测试链表数据结构
- 利用 GDB 排查内存泄漏问题
- 研究 Google Mock 进行模拟对象测试
- 引入 Google Benchmark 进行性能分析
- 配置 CI 流水线实现自动化测试
持续投入测试与调试工作,是提升工程素养、保障软件稳定性的必经之路。


