跳到主要内容
C++ 测试与调试:确保代码质量与稳定性 | 极客日志
C++
C++ 测试与调试:确保代码质量与稳定性 综述由AI生成 C++ 测试与调试是保障软件稳定性的关键环节。内容涵盖单元测试框架(Google Test、Catch2)的使用、GDB 及 Visual Studio 调试技巧,并通过计算器案例演示了从项目结构搭建到集成测试的全流程。掌握这些实践方法能有效提升代码质量与可维护性。
月光旅人 发布于 2026/3/23 更新于 2026/5/1 3 浏览C++ 测试与调试:确保代码质量与稳定性
在实际开发中,代码写得通只是第一步,能稳定运行且易于维护才是关键。本章我们将深入探讨 C++ 测试与调试的核心知识,帮助你建立保障代码质量的思维体系。
学习目标
通过本章学习,你将掌握测试的基本概念与分类,熟悉 Google Test 和 Catch2 等主流单元测试框架,理解集成测试的重要性,并学会使用 GDB 和 Visual Studio 调试器定位问题。最终目标是培养测试驱动开发的思维,设计出高质量的代码。
测试的基本概念
测试的分类
测试通常分为以下几类,覆盖从微观到宏观的不同层面:
单元测试 :针对单个函数或类的功能进行测试。
集成测试 :验证多个模块组合后的集成功能是否正常。
系统测试 :对整个系统的功能进行全面测试。
验收测试 :确认系统是否满足用户的业务需求。
性能测试 :评估系统的响应时间、吞吐量等性能指标。
测试原则
为了保证测试的有效性,建议遵循以下原则:
尽早测试 :在开发早期介入,降低修复成本。
全面覆盖 :尽可能覆盖所有可能的场景和边界条件。
自动化 :将重复的测试过程自动化,提高回归效率。
可重复性 :测试结果应稳定可靠,便于复现。
独立性 :测试用例之间应尽量互不干扰。
单元测试框架
Google Test 框架
Google Test (gtest) 是业界广泛使用的 C++ 单元测试框架,提供了丰富的断言和测试宏。
安装指南
sudo apt-get install libgtest-dev
brew install googletest
示例代码
下面是一个典型的测试结构,展示了如何测试 MyClass 的构造函数及方法:
#include <gtest/gtest.h>
#include "MyClass.h"
TEST (MyClassTest, ConstructorTest) {
MyClass obj;
EXPECT_EQ (obj.getValue (), );
}
(MyClassTest, SetGetValueTest) {
MyClass obj;
obj. ( );
(obj. (), );
}
(MyClassTest, AddTest) {
MyClass obj;
obj. ( );
result = obj. ( );
(result, );
(obj. (), );
}
(MyClassTest, SubtractTest) {
MyClass obj;
obj. ( );
result = obj. ( );
(result, );
(obj. (), );
}
{
::testing:: (&argc, argv);
();
}
0
TEST
setValue
42
EXPECT_EQ
getValue
42
TEST
setValue
10
int
add
20
EXPECT_EQ
30
EXPECT_EQ
getValue
30
TEST
setValue
50
int
subtract
20
EXPECT_EQ
30
EXPECT_EQ
getValue
30
int main (int argc, char ** argv)
InitGoogleTest
return
RUN_ALL_TESTS
Catch2 框架 Catch2 是另一个流行的选择,语法更简洁,功能也很丰富。
sudo apt-get install catch2
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 program.cpp -o program
gdb program
break 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
核心代码 #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
#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;
}
#ifndef PARSER_H
#define PARSER_H
#include <string>
#include "Calculator.h"
class Parser {
public :
int parse (const std::string& expression) ;
};
#endif
#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 ("无效的运算符" );
}
}
#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 ;
}
#include <gtest/gtest.h>
#include "Calculator.h"
TEST (CalculatorTest, AddTest) {
EXPECT_EQ (Calculator::add (10 , 20 ), 30 );
EXPECT_EQ (Calculator::add (-10 , 20 ), 10 );
EXPECT_EQ (Calculator::add (0 , 0 ), 0 );
}
TEST (CalculatorTest, SubtractTest) {
EXPECT_EQ (Calculator::subtract (20 , 10 ), 10 );
EXPECT_EQ (Calculator::subtract (10 , 20 ), -10 );
EXPECT_EQ (Calculator::subtract (0 , 0 ), 0 );
}
TEST (CalculatorTest, MultiplyTest) {
EXPECT_EQ (Calculator::multiply (10 , 20 ), 200 );
EXPECT_EQ (Calculator::multiply (-10 , 20 ), -200 );
EXPECT_EQ (Calculator::multiply (0 , 10 ), 0 );
}
TEST (CalculatorTest, DivideTest) {
EXPECT_EQ (Calculator::divide (20 , 10 ), 2 );
EXPECT_EQ (Calculator::divide (-20 , 10 ), -2 );
EXPECT_THROW (Calculator::divide (10 , 0 ), std::invalid_argument);
}
#include <gtest/gtest.h>
#include "Parser.h"
TEST (ParserTest, ParseTest) {
Parser parser;
EXPECT_EQ (parser.parse ("10 + 20" ), 30 );
EXPECT_EQ (parser.parse ("20 - 10" ), 10 );
EXPECT_EQ (parser.parse ("10 * 20" ), 200 );
EXPECT_EQ (parser.parse ("20 / 10" ), 2 );
EXPECT_THROW (parser.parse ("10 / 0" ), std::invalid_argument);
EXPECT_THROW (parser.parse ("10 a 20" ), std::invalid_argument);
}
#include <gtest/gtest.h>
#include "Parser.h"
TEST (IntegrationTest, CalculateTest) {
Parser parser;
EXPECT_EQ (parser.parse ("10 + 20" ), 30 );
EXPECT_EQ (parser.parse ("20 - 10" ), 10 );
EXPECT_EQ (parser.parse ("10 * 20" ), 200 );
EXPECT_EQ (parser.parse ("20 / 10" ), 2 );
EXPECT_THROW (parser.parse ("10 / 0" ), std::invalid_argument);
}
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)
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 调试内存泄漏问题。
使用 Visual Studio 调试器调试多线程程序。
实现一个类,并使用 Catch2 测试其功能。
编写集成测试,验证一个简单的银行账户系统。
进阶挑战
研究如何使用 Google Mock 进行模拟对象测试。
学习使用 Google Benchmark 进行性能基准测试。
探索 gcov 等代码覆盖率工具的使用。
了解 Clang Static Analyzer 等静态分析工具。
配置 GitHub Actions 实现持续集成自动化测试。
相关免费在线工具 Base64 字符串编码/解码 将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
Base64 文件转换器 将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online
Markdown转HTML 将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online
HTML转Markdown 将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online
JSON 压缩 通过删除不必要的空白来缩小和压缩JSON。 在线工具,JSON 压缩在线工具,online
JSON美化和格式化 将JSON字符串修饰为友好的可读格式。 在线工具,JSON美化和格式化在线工具,online