前言
由于工作需要,要使用googletest做单元测试,本文记录下搭建gtest单元测试环境,并查看代码覆盖率的方法,以备不时之需。
准备工作
编译gtest
克隆gtest源码:
git clone https://github.com/google/googletest.git
使用缺省分支main,并在googletest源码当前目录创建一个编译的build目录:
mkdir -p build && cd build
进入到build目录下,执行如下命令:
cmake .. && make -j9 && make install DESTDIR=./
编译安装完成,gtest相关头文件在以下目录,其中lib和usr目录在googletest根目录:
├── lib
│ ├── libgmock.a
│ ├── libgmock_main.a
│ ├── libgtest.a
│ └── libgtest_main.a
└── usr
└── local
├── include
│ ├── gmock
│ └── gtest
└── lib
├── cmake
├── libgmock.a
├── libgmock_main.a
├── libgtest.a
├── libgtest_main.a
└── pkgconfig
安装lcov
macOS下需要使用lcov来生成单元测试的覆盖率报告,用brew下载即可:
brew install lcov
跑demo
建qt工程
至此, 万事俱备,现在使用qt创建一个c++的工程:
进入到demo的工程根目录,将googletest编译安装之后的include和lib文件copy至demo的当前根目录下,注意gtest目录下便是依赖文件:
mingo@localhost:~/Applications/workspace/tools/unit_test$tree -L 3
.
├── build-cpp-unit-test-Desktop_arm_darwin_generic_mach_o_64bit-Debug
│ ├── CMakeCache.txt
│ ├── CMakeCache.txt.prev
│ ├── CMakeFiles
│ │ ├── 3.26.0
│ │ ├── CMakeConfigureLog.yaml
│ │ ├── TargetDirectories.txt
│ │ ├── cmake.check_cache
│ │ ├── cpp-unit-test.dir
│ │ ├── pkgRedirects
│ │ └── rules.ninja
│ ├── Testing
│ │ └── Temporary
│ ├── build.ninja
│ ├── cmake_install.cmake
│ ├── cpp-unit-test
│ ├── output
│ │ ├── Users
│ │ ├── amber.png
│ │ ├── cmd_line
│ │ ├── emerald.png
│ │ ├── gcov.css
│ │ ├── glass.png
│ │ ├── index-sort-f.html
│ │ ├── index-sort-l.html
│ │ ├── index.html
│ │ ├── ruby.png
│ │ ├── snow.png
│ │ ├── updown.png
│ │ └── v1
│ ├── qtcsettings.cmake
│ └── test.info
├── cpp-unit-test
│ ├── CMakeLists.txt
│ ├── CMakeLists.txt.user
│ └── main.cpp
└── gtest
├── include
│ ├── gmock
│ └── gtest
└── lib
├── libgmock.a
├── libgmock_main.a
├── libgtest.a
└── libgtest_main.a
17 directories, 29 files
编写CMakeLists
然后编写CMakeLists构建脚本:
cmake_minimum_required(VERSION 3.5)
project(cpp-unit-test LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
# lcov相关编译选项
#SET(GCC_COVERAGE_COMPILE_FLAGS "-g -O0 -coverage -fprofile-arcs -ftest-coverage")
SET(GCC_COVERAGE_COMPILE_FLAGS "-g -O0 -fprofile-arcs -ftest-coverage")
#SET(GCC_COVERAGE_LINK_FLAGS "-coverage -lcov")
#SET(GCC_COVERAGE_LINK_FLAGS "-coverage")
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${GCC_COVERAGE_COMPILE_FLAGS}")
SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${GCC_COVERAGE_LINK_FLAGS}")
set(GTEST_ROOT_DIR ${PROJECT_SOURCE_DIR}/../gtest)
set(GTEST_INCLUDE_DIR ${GTEST_ROOT_DIR}/include)
set(GTEST_LIB_DIR ${GTEST_ROOT_DIR}/lib)
message("dir=${PROJECT_SOURCE_DIR}")
include_directories(${GTEST_INCLUDE_DIR})
#link_libraries(${GTEST_LIB_DIR})
set(GTEST_LIBS ${GTEST_LIB_DIR}/libgtest.a ${GTEST_LIB_DIR}/libgtest_main.a ${GTEST_LIB_DIR}/libgmock.a ${GTEST_LIB_DIR}/libgmock_main.a)
#target_link_libraries(cpp-unit-test gcov)
add_executable(cpp-unit-test main.cpp)
target_link_libraries(cpp-unit-test PRIVATE ${GTEST_LIBS})
install(TARGETS cpp-unit-test
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR})
写一个最简单的测试用例:
#include <iostream>
using namespace std;
#include "gtest/gtest.h"
#include "gmock/gmock.h"
TEST(mytest, mytest1) {
EXPECT_TRUE(1);
}
int main()
{
cout << "Hello World!" << endl;
return RUN_ALL_TESTS();
}
运行
代码覆盖率
在上图的qt工程命令行里,可以看到用例的通过数和失败数,但还不够直观,此处使用lcov来生成html网页形式查看代码覆盖率:
进入到demo的构建目录下:
mingo@localhost:~/Applications/workspace/tools/unit_test/build-cpp-unit-test-Desktop_arm_darwin_generic_mach_o_64bit-Debug$ll
total 3352
drwxr-xr-x 16 mingo staff 512 6 15 14:29 ./
drwxr-xr-x 5 mingo staff 160 6 15 13:30 ../
drwxr-xr-x 3 mingo staff 96 6 15 13:30 .cmake/
-rw-r--r-- 1 mingo staff 149532 6 15 14:26 .ninja_deps
-rw-r--r-- 1 mingo staff 383 6 15 14:26 .ninja_log
drwxr-xr-x 4 mingo staff 128 6 15 13:30 .qtc_clangd/
-rw-r--r-- 1 mingo staff 13148 6 15 13:30 CMakeCache.txt
-rw-r--r-- 1 mingo staff 13148 6 15 13:30 CMakeCache.txt.prev
drwxr-xr-x 9 mingo staff 288 6 15 14:26 CMakeFiles/
drwxr-xr-x 3 mingo staff 96 6 15 13:30 Testing/
-rw-r--r-- 1 mingo staff 11042 6 15 14:26 build.ninja
-rw-r--r-- 1 mingo staff 2238 6 15 13:30 cmake_install.cmake
-rwxr-xr-x 1 mingo staff 1416352 6 15 14:26 cpp-unit-test*
drwxr-xr-x 15 mingo staff 480 6 15 14:29 output/
-rw-r--r-- 1 mingo staff 52 6 15 14:26 qtcsettings.cmake
-rw-r--r-- 1 mingo staff 25803 6 15 14:28 test.info
然后执行如下命令:
lcov -c -o test.info -d .
报了如下错:
geninfo: ERROR: (inconsistent) "gtest-internal.h":454: function _ZN7testing8internal15TestFactoryImplI19mytest_mytest1_TestEC1Ev end line 444 less than start line 454. Cannot derive function end line. See lcovrc man entry for 'derive_function_end_line'.
(use "geninfo --ignore-errors inconsistent ..." to bypass this error)
解决办法:
lcov -c -o test.info -d . --ignore-errors inconsistent
可以看到,执行成功之后,会在当前目录下生成test.info文件:
test.info
然后,在当前目录下创建output目录,用来存放覆盖率报告相关的html文件:
mkdir -p output && genhtml test.info -o ./output --ignore-errors inconsistent
以上命令执行成功,会在如下output目录下产生html相关的文件:
mingo@localhost:~/Applications/workspace/tools/unit_test/build-cpp-unit-test-Desktop_arm_darwin_generic_mach_o_64bit-Debug$tree -L 2
.
├── CMakeCache.txt
├── CMakeCache.txt.prev
├── CMakeFiles
│ ├── 3.26.0
│ ├── CMakeConfigureLog.yaml
│ ├── TargetDirectories.txt
│ ├── cmake.check_cache
│ ├── cpp-unit-test.dir
│ ├── pkgRedirects
│ └── rules.ninja
├── Testing
│ └── Temporary
├── build.ninja
├── cmake_install.cmake
├── cpp-unit-test
├── output
│ ├── Users
│ ├── amber.png
│ ├── cmd_line
│ ├── emerald.png
│ ├── gcov.css
│ ├── glass.png
│ ├── index-sort-f.html
│ ├── index-sort-l.html
│ ├── index.html
│ ├── ruby.png
│ ├── snow.png
│ ├── updown.png
│ └── v1
├── qtcsettings.cmake
└── test.info
10 directories, 22 files
进入到output目录下,执行如下命令:
open index.html