本文主要介绍了我在开发中引入单元测试的实践

引入单元测试到项目的思路

由于我自己基本都是在windows下进行开发,所以这里只介绍windows下的搭建方法

一般的嵌入式产品开发中,涉及的内容为最左侧虚线框内的内容,我这里初步进行了划分。总之,完成了虚线框内的内容,最后编译输出的二进制文件就是最终产品的交付程序了;

如果引入单元测试到项目中的话,我们需要单独开辟一个文件夹,用来存放单元测试独有的部分,单元测试测试的是嵌入式的软件功能,所以左侧的产品程序大部分也都是需要纳入到测试工程中的。

特别的,嵌入式产品的程序是唯一的,产品构建和测试构建编译都使用了这部分内容,产品的构建使用的是平台对应的编译器,而测试的构建编译使用gcc编译器,最终测试部分编译输出exe可执行文件,我们可以在PC端执行运行相关测试。

环境准备

准备gcc编译器的环境

首先,如果进行PC端测试,编译器必然是使用PC端预先编译的,考虑到跨平台和通用程度,我决定使用gcc编译器作为PC端的编译器;

想要在在windows下进行gcc的程序开发,需要准备以下软件:

  • MinGW 是一个用于 Windows 平台的开发工具集,它提供了一组 GNU 工具和库,可以用于编译和构建本地的 Windows 应用程序
  • Cmake C/C++ 自动化构建工具
  • (可选)一个shell环境,可以选择安装mysys2,我这里使用的是ConEmu
  • (可选)Python3,并安装gcovr库,用来执行覆盖率的报告输出

编译GoogleTest库

步骤1.克隆官方仓库

1
git clone https://github.com/google/googletest.git

步骤2.编译

CmakeList.txt文件在项目文件夹下

如果使用CodeBlock/Visual Studio/Clion等IDE:

  • 直接加载CmakeList.txt工程编译,在IDE中配置好gcc编译器路径,进行编译

如果使用ConEmu等虚拟环境,配置好环境变量,主要是gcc的路径和Cmake的路径,进入到项目目录下,进行编译

1
2
3
4
5
cd googletest
mkdir build
cd build
cmake ..
make

编译成功后,会在项目的build/lib文件夹下生成静态库,分别为libgtest.a、libgtest_main.a、libgmock.a、libgmock_main.a

创建含有单元测试的项目

步骤3. 创建工程结构目录,增加工程源码

以自己项目文件结构为例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
Project
├─ i01_source_code
├─ ..
└─ app
├─ basic_algorithm.c
└─ main.c
└─ i02_unit_test
├─ build
├─ gtest
├─ ..
├─ gtest.h
└─ lib
├─ ..
├─ libgtest.a
└─ libgtest_main.a
├─ runTest.cc
├─ testBasicAlgorithm.cc
└─ CMakeLists.txt
└─ i09_publish
├─ build
└─ CMakeLists.txt

source_code 用来存放项目的源码,只有.c/.h和汇编文件
publish 用来存放项目的构建文件,最终产品的输出程序在这个文件夹下

步骤4. 拷贝googletest库到工程目录

unit_test 用来存放单元测试的测试用例以及构建文件,将googletest工程下的四个静态库文件复制到工程的gtest文件目录下(直接复制lib文件夹);还要将googletest源码中的所有头文件(googletest/googletest/include/gtest目录下)复制过来,

步骤5. 编写单元测试的构建CmakeList.txt

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
cmake_minimum_required(VERSION 3.14)
project(embeded-testing-demo
DESCRIPTION "google test for embeded code"
)

# Coverage flags for GCC/Clang
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --coverage")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} --coverage")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} --coverage")

set(TEST_NAME runTests)
set(SOURCE_DIR ../i01_source_code)

include_directories(
.
${SOURCE_DIR}
${SOURCE_DIR}/app
)

add_executable(${TEST_NAME}
runTest.cc
${SOURCE_DIR}/app/basic_algorithm.c
)

target_link_directories(${TEST_NAME}
PRIVATE
gtest/lib
)

target_link_libraries(${TEST_NAME}
PRIVATE
gmock
gmock_main
gtest
gtest_main
)

在这个构建里,主要是定位好gtest库的位置和头文件目录,以及将工程源码中需要参与测试的文件添加进来。

步骤6. 编译单元测试程序并执行

到这一步就比较容易了,就跟喝水一样了,我使用的shell命令跳转到unit_test文件夹下,直接执行:

1
2
3
cd build
cmake ..
make

执行完成后会在build文件夹下输出runTest.exe,执行runTest.exe

1
runTest.exe

利用gcvor导出单元测试覆盖率报告(可选)

步骤7.安装gcovr库

1
pip install gcovr

步骤8. 编写导出配置

在unit_test工程目录下,新建gcovr.cfg文件

1
2
3
4
root = ../../
exclude = gtest/
html-detail = yes
output = build/coverage.html

指定需要导出的根目录,不包含在内的文件、导出html模式、输出路径四个关键配置;因为gtest内容实际与项目无关,所以被剔除了。

步骤9. 执行命令

1
gcovr --config ../gcovr.cfg

生成html报告后,在build文件夹下打开即可: