使用gtest做cpp的单元测试并可查看代码覆盖率

前言

由于工作需要,要使用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编译安装之后的includelib文件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
相关推荐
surfirst10 小时前
举例说明 .Net Core 单元测试中 xUnit 的 [Theory] 属性的用法
单元测试·.netcore·xunit
回眸&啤酒鸭1 天前
【回眸】Tessy 单元测试软件使用指南(四)常见报错及解决方案与批量初始化的经验
单元测试·tessy
Iam傅红雪2 天前
mock数据,不使用springboot的单元测试
spring boot·后端·单元测试
月光code2 天前
SLF4J报错log4j又报错
单元测试·log4j
编程经验分享3 天前
Spring Boot 基于 Mockito 单元测试
spring boot·后端·单元测试
神即道 道法自然 如来3 天前
测试面试题:请你分别介绍一下单元测试、集成测试、系统测试、验收测试、回归测试
单元测试·集成测试
友恒3 天前
C#单元测试(一):用 NUnit 和 .NET Core 进行单元测试
单元测试·c#·.netcore
进击的横打4 天前
【车载开发系列】ParaSoft单元测试环境配置(四)
c语言·单元测试
wangyue44 天前
C# MSTest 进行单元测试
单元测试
互联网杂货铺6 天前
软件测试之单元测试/系统测试/集成测试详解
自动化测试·软件测试·python·测试工具·单元测试·测试用例·集成测试