1 前言
由于工作需要,要使用googletest做单元测试,本文记录下搭建gtest单元测试环境,并查看代码覆盖率的方法,以备不时之需。
2 准备工作
2.1 编译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
2.2 安装lcov
macos下需要使用lcov来生成单元测试的覆盖率报告,用brew下载即可:
brew install lcov
3 跑demo
3.1 cmake构建
3.1.1 建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
3.1.2 编写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();
}
3.1.3 运行
3.1.4 代码覆盖率
在上图的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
3.2 xcode构建
3.2.1 安装工具
用brew工具安装以下2个llvm相关的工具:
brew install llvm
brew install llvm-cov
并将安装目录bin配置到path环境变量中去。,然后执行以下命令生效:
source ~/.bash_profile
3.2.2 环境搭建
首先,用xcode创建一个纯c++的target工程,如下:
并增加编译选项:
在target的build settings => apple clang-custom compiler flags => other c flags / other c++ flags 2栏增加以下2个编译选项:
-fprofile-instr-generate
-fcoverage-mapping
并在build settings => build options => enable code coverage support => yes
在build phases => link binary with libraries 增加llvm覆盖率的库文件
libclang_rt.profile_osx.a
m1芯片下,此库在llvm安装后生成在以下目录:
/applications/xcode.app/contents/developer/toolchains/xcodedefault.xctoolchain/usr/lib/clang/15.0.0/lib/darwin/
3.2.3 代码覆盖率
运行xcode程序后,到可执行文件所在目录执行
./video_cache_unittest
而后会生成default.profraw文件
xcrun llvm-cov show ./video_cache_unittest -instr-profile=default.profdata -format=html -output-dir=coverage_report
找到生成的 .profraw
文件,合并它们生成 .profdata
文件:
xcrun llvm-profdata merge -sparse default.profraw -o default.profdata
使用 llvm-cov
工具生成 html 报告:
xcrun llvm-cov show ./video_cache_unittest -instr-profile=default.profdata -format=html -output-dir=coverage_report
最后生成的覆盖率目录结构大致如下:
mingo@localhost:~/library/developer/xcode/deriveddata/video_cache_proxy-dtlzrjkxnibrjmeyqwheuwsphlau/build/products/debug$tree -l 3
.
├── coverage_report
│ ├── coverage
│ │ └── users
│ ├── index.html
│ └── style.css
├── coverage_report.txt
├── default.profdata
├── default.profraw
├── errorlog
├── libvideo_cache.a
├── video_cache_unittest
└── xactlog
再进入到coverage_report目录下,执行以下命令:
open index.html
就可以看到单元测试的覆盖率报告了:
发表评论