C++ gtest单元测试
1. 单元测试
单元测试是软件开发过程中的一种测试方法,用于验证程序中的最小可测单元,通常是方法、类和模块等。它的目的是确保每个单元都能正确执行其预定义的功能,并且其他功能单元之间的交互符合预期。
1.1. 单元测试介绍
1.1.1. 为什么需要单元测试
- 早期发现问题:在软件开发的早期阶段发现潜在的问题和错误,避免后续开发过程中的不必要的麻烦。
- 提高代码质量:有助于提高代码质量,减少bug,增强代码可维护性。
- 提高开发效率:支持重构和修改,提高开发效率。
1.1.2. 单元测试的类型
- 静态测试:在不执行程序的情况下对代码进行分析和检查的方法,包括代码审查、代码走查和静态分析工具的使用。
- 动态测试:通过执行程序并观察其行为来测试软件的过程,包括白盒测试和黑盒测试。
1.1.3. 常用的C++单元测试框架
- Google Test:由Google开发,支持多种测试模式,如测试夹具、参数化测试等,并且具有良好的跨平台特性。
- Catch2:一个轻量级的测试框架,支持行为驱动开发(BDD)风格的测试。
- Boost.Test:Boost库的一部分,功能强大且灵活,适合大型项目的测试。
- CppUnit:类似于JUnit的框架,适用于C++。
1.1.4. 单元测试的实施步骤
- 编写测试用例:根据需求编写测试用例,模拟各种输入情况,验证函数的输出是否符合预期。
- 运行测试:使用测试框架提供的工具运行测试用例,观察测试结果。
- 分析结果:如果测试通过,说明代码在这些情况下工作正常;如果失败,则需要调试和修复。
1.1.5. 单元测试的最佳实践
- 早期介入:在软件开发的早期阶段就开始编写测试用例。
- 持续集成:结合持续集成工具,在每次代码提交后自动执行测试。
- 编写清晰的测试用例:测试用例应该尽量简单明了,避免复杂的逻辑。
本文将介绍Google Test框架的基本使用方法,包括安装、配置、编写测试用例和运行测试等步骤。
1.2. 安装Google Test
vcpkg install gtest
1.3. 编写代码
我们的代码目录结构如下:
C:.
| CMakeLists.txt
| output.txt
| run.ps1
| tests_output.txt
|
+---.vscode
| c_cpp_properties.json
| settings.json
|
+---include
| CMath.h
| common.h
|
+---lib
| bay.lib
|
+---src
| CMath.cpp
| main.cpp
|
\---testmain.cpp
1.3.1. CMath
我们写一个最简单的加法函数:
int CMath::add(int a, int b)
{return a + b;
}
1.3.2. src/main.cpp
这是咱们正常软件的入口函数,我们在这里调用CMath的add函数:
#include <iostream>
#include "CMath.h"int main() {CMath math;std::cout << math.add(1,2) << std::endl;return 0;
}
1.3.3. test/main.cpp
这是我们的单元测试代码,我们在这里调用CMath的add函数,并且使用Google Test提供的断言来验证结果:
#include <gtest/gtest.h>
#include "common.h"
#include "CMath.h"// 测试用例
TEST(AdditionTest, PositiveNumbers) {CMath math;EXPECT_EQ(math.add(1, 2), 3);
}TEST(AdditionTest, NegativeNumbers) {CMath math;EXPECT_EQ(math.add(-1, -2), -4); //单元测试结果会报错
}int main(int argc, char **argv) {::testing::InitGoogleTest(&argc, argv);return RUN_ALL_TESTS();
}
1.4. CMakeLists.txt
我们使用CMake来构建我们的项目,将生成可执行文件demo.exe和单元测试可执行文件runUnitTests.exe。
特别注意:
- 在生成runUnitTests.exe时,需要包含test/.cpp 和 src/.cpp ,同时排除掉src/main.cpp,否则会报错。
- 建议在test/main.cpp 中使用main(),这样链接时用
GTest::gtest
,而不要用GTest::gtest_main
,否则会报错。
CMakeLists.txt如下:
# 指定CMake的最低版本要求
cmake_minimum_required(VERSION 3.10)# 设置项目名称和语言
project(demo LANGUAGES CXX)# 设置C++标准为C++17
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON) # 强制要求使用指定的C++标准# 查找外部依赖包 fmt 和 spdlog
find_package(fmt CONFIG REQUIRED)
find_package(spdlog CONFIG REQUIRED)# 递归查找src目录下所有的cpp源文件,作为主程序源文件
file(GLOB_RECURSE SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp")#创建主程序可执行文件
add_executable(${PROJECT_NAME} ${SOURCES})# 启用测试功能
enable_testing()# 递归查找test目录下所有的cpp文件,作为单元测试源文件
file(GLOB_RECURSE UNIT_TEST_SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/test/*.cpp")
# 查找src目录下除main.cpp外的所有cpp文件,供测试用例复用
file(GLOB_RECURSE UNIT_TEST_SRC_SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp")
list(FILTER UNIT_TEST_SRC_SOURCES EXCLUDE REGEX "src/main.cpp")
# 创建测试可执行文件 runUnitTests
add_executable(runUnitTests ${UNIT_TEST_SOURCES} ${UNIT_TEST_SRC_SOURCES})# 查找GTest库
find_package(GTest CONFIG REQUIRED)
# 链接GTest和GMock库到测试可执行文件
# 注意:这里只需链接gtest和gmock,不要链接gtest_main/gmock_main,否则main函数会冲突
target_link_libraries(runUnitTests PRIVATE GTest::gtest GTest::gmock )# 添加CTest测试
add_test(AllTestsInMain runUnitTests)# 包含 GoogleTest 模块,自动发现并添加测试
include(GoogleTest)
gtest_discover_tests(runUnitTests)# 设置主程序包含目录
# PRIVATE表示这些包含目录仅在当前目标内部可见
# include/bay目录如不存在可去除
# 下面两处都可根据实际情况调整# 主程序包含目录
target_include_directories(${PROJECT_NAME} PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/include")
# 测试程序包含目录
target_include_directories(runUnitTests PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/include")# 链接外部依赖库到主程序
# PRIVATE表示依赖不会传递给链接此目标的其他目标
# 如lib/bay.lib不存在可去除
# fmt和spdlog为必需target_link_libraries(${PROJECT_NAME} PRIVATE fmt::fmt # 格式化库spdlog::spdlog # 日志库
)target_link_libraries(runUnitTests PRIVATE fmt::fmt # 格式化库spdlog::spdlog # 日志库
)
1.5. 编译
# 生成构建目录并配置CMake工程
cmake -B build -S . -DCMAKE_TOOLCHAIN_FILE="${VCPKG_ROOTDIR}/scripts/buildsystems/vcpkg.cmake" # 编译工程,若成功则运行生成的可执行文件
cmake --build build ;
1.6. 运行单元测试
有以下方式可以进行单元测试
1.6.1. 直接运行 runUnitTests.exe
输出结果如下:
3
[==========] 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
C:\Users\Admin\05.gtest\test\main.cpp(13): error: Expected equality of these values:math.add(-1, -2)Which is: -3-4[ FAILED ] AdditionTest.NegativeNumbers (0 ms)
[----------] 2 tests from AdditionTest (0 ms total)[----------] Global test environment tear-down
[==========] 2 tests from 1 test suite ran. (1 ms total)
[ PASSED ] 1 test.
[ FAILED ] 1 test, listed below:
[ FAILED ] AdditionTest.NegativeNumbers1 FAILED TEST
有一条测试用例失败,是因为为我们特意在test/main.cpp中将输出结果设置错误。
1.6.2. 运行 CTest
cd build
ctest
# 也可采用 ctest -V ,这样输出的结果会包含详细信息
输出结果如下:
Test project C:/Users/Admin/05.gtest/buildStart 1: AdditionTest.PositiveNumbers
1/3 Test #1: AdditionTest.PositiveNumbers ..... Passed 0.09 secStart 2: AdditionTest.NegativeNumbers
2/3 Test #2: AdditionTest.NegativeNumbers .....***Failed 0.01 secStart 3: AllTestsInMain
3/3 Test #3: AllTestsInMain ...................***Failed 0.01 sec33% tests passed, 2 tests failed out of 3Total Test time (real) = 0.13 secThe following tests FAILED:2 - AdditionTest.NegativeNumbers (Failed)3 - AllTestsInMain (Failed)
Errors while running CTest
1.6.3. visual studio code 中用插件进行单元测试
先安装插件
然后点击插件中的单元测试,点击相关测试用例,运行即可。