当前位置:首页 > 公众号精选 > 21ic电子网
[导读]前言测试是软件开发过程中一个必须的环节,测试确保软件的质量符合预期。对于工程师自己来说,单元测试也是提升自信心的一种方式。直接交付没有经过测试的代码是不太好的,因为这很可能会浪费整个团队的时间,在一些原本早期就可以发现的问题上。而单元测试,就是发现问题一个很重要的环节。本文以C语...

前言

测试是软件开发过程中一个必须的环节,测试确保软件的质量符合预期。

对于工程师自己来说,单元测试也是提升自信心的一种方式。

直接交付没有经过测试的代码是不太好的,因为这很可能会浪费整个团队的时间,在一些原本早期就可以发现的问题上。而单元测试,就是发现问题一个很重要的环节。

本文以C 语言为基础,讲解如何进行单元测试并生成测试报告。

在工具上,我们会使用下面这些:

  • GCC
  • CMake
  • Google Test
  • gcov
  • lcov

演示项目

为了方便本文的讲解,我专门编写了一个演示项目作为代码示例。

演示项目的源码可以在我的Github上获取:paulQuei/gtest-and-coverage[1]

你可以通过下面几条命令下载和运行这个项目:

git clone https://github.com/paulQuei/gtest-and-coverage.git
cd gtest-and-coverage
./make_all.sh
要运行这个项目,你的机器上必须先安装好前面提到的工具。如果没有,请阅读下文以了解如何安装它们。

如果你使用的是Mac系统,下文假设你的系统上已经安装了brew[2]包管理器。如果没有,请通过下面这条命令安装它:

/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"

项目结构

演示项目的目录结构如下:

.
├── CMakeLists.txt
├── googletest-release-1.8.1.zip
├── include
│   └── utility.h
├── make_all.sh
├── src
│   └── utility.cpp
└── test
    └── unit_test.cpp
这里演示的内容是:以测试一个我们要提供的软件库为例,讲解如何对其进行单元测试并生成测试报告。

为了简单起见,这个软件库只有一个头文件和一个实现文件。

当然,在实际上的项目中,一个软件库会通常包含更多的文件,不过这并不影响我们要说明的问题。

演示项目中的文件说明如下:

文件名称说明
make_all.sh入口文件,会执行:编译,测试和生成报告等所有工作
CMakeLists.txt项目的编译文件
googletest-release-1.8.1.zipgoogle test源码压缩包
utility.h待测试的软件库的头文件
utility.cpp待测试的软件库的实现文件
unit_test.cpp对软件库进行单元测试的代码

测试环境

演示项目在如下的环境中测试过。

  • MacBook Pro
    • 操作系统:macOS Mojave 10.14.1
    • 编译器:Apple LLVM version 10.0.0 (clang-1000.11.45.2)
    • CMake:cmake version 3.12.1
    • Google Test: 1.8.1
    • lcov: lcov version 1.13
  • Ubuntu
    • 操作系统:Ubuntu 16.04.5 LTS
    • 编译器:gcc (Ubuntu 5.4.0-6ubuntu1~16.04.10) 5.4.0 20160609
    • CMake:cmake version 3.5.1
    • Google Test:1.8.1
    • lcov:lcov version 1.12

关于CMake

为了简化编译的过程,这里使用CMake作为编译工具。关于CMake的更多内容请参见请官网:https://cmake.org[3]

关于如何安装CMake请参见这里:Installing CMake[4]

另外,你也可以通过一条简单的命令来安装CMake:

  • Mac系统:
brew install cmake
  • Ubuntu系统
sudo apt install cmake
由于篇幅所限,这里不打算对CMake做过多讲解,读者可以访问其官网或者在网络上搜寻其使用方法。

这里仅仅对演示项目中用到的内容做一下说明。演示项目中的CMakeLists.txt内容如下:

cmake_minimum_required(VERSION 2.8.11) ①
project(utility) ②

set(CMAKE_CXX_STANDARD 11) ③

set(GTEST googletest-release-1.8.1) ④
include_directories("./include" "${GTEST}/googletest/include/")
link_directories("build/gtest/googlemock/gtest/")

SET(CMAKE_CXX_FLAGS  "${CMAKE_CXX_FLAGS} --coverage") ⑤

add_library(${CMAKE_PROJECT_NAME}_lib src/utility.cpp) ⑥

add_executable(unit_test test/unit_test.cpp) ⑦
target_link_libraries(unit_test ${CMAKE_PROJECT_NAME}_lib gtest gtest_main pthread) ⑧
以编号为序,这段代码说明如下:

  1. 设置使用的CMake最低版本号为2.8.11。
  2. 指定项目的名称为”utility”,项目名称可以通过${CMAKE_PROJECT_NAME}进行引用。
  3. 指定使用C 11。
  4. 这里的三行是编译google test,并将其头文件路径和编译结果的库文件路径添加到环境中。因为后面在编译单元测试代码的时候需要用到。
  5. 添加--coverage到编译器flag中,这个参数是很重要的,因为这是生成代码覆盖率所必须的。关于该编译参数的说明见这里:Program Instrumentation Options[5]
  6. 编译我们的软件库,这里将生成libutility_lib.a库文件。
  7. 编译单元测试的可执行文件。
  8. 单元测试的可执行文件需要链接我们开发的软件库以及google test的库。另外,google test依赖了pthread,所以这个库也需要。

关于测试

软件测试有很多种分类方式。从测试的级别来说,可以大致分为:

  • 单元测试
  • 集成测试
  • 系统测试
这其中,单元测试是最局部和具体的。它通常需要对代码中的每一个类和函数进行测试。

单元测试通常由开发者完成,需要针对代码逻辑进行测试。所以它是一种白盒测试[6]

关于xUnit

xUnit是几种单元测试框架的总称。最早源于Smalltalk的单元测试框架SUnit,它是由Kent Beck[7]开发的。

除此之外,还有针对Java语言的JUnit,针对R语言的RUnit。

在本文中,我们使用Google开发的xUnit框架:Google Test。

Google Test介绍

Google Test的项目主页在Github上:Github: Google Test[8]

实际上,这个项目中同时包含了GoogleTest和GoogleMock两个工具,本文中我们只会讲解第一个。

Google Test支持的操作系统包含下面这些:

  • Linux
  • Mac OS X
  • Windows
  • Cygwin
  • MinGW
  • Windows Mobile
  • Symbian
目前有很多的项目都使用了Google Test,例如下面这些:

  • Chromium projects[9]
  • LLVM[10]
  • Protocol Buffers[11]
  • OpenCV[12]
  • tiny-dnn[13]

编译Google Test

关于如何编译Google Test请参见这里:Generic Build Instructions[14]

为了便于读者使用,我们在演示项目中包含了Google Test 1.8.1的源码压缩包。并且在CMake文件中,同时包含了Google Test的编译和使用配置工作。

如果使用演示项目,读者将不需要手动处理Google Test的编译和安装工作。

使用Google Test

演示项目代码说明

为了便于下文说明,演示项目中包含了几个简单的函数。

可以从这里下载源码以便查看其中的内容:paulQuei/gtest-and-coverage[15]

演示项目中的软件库包含一个头文件和一个实现文件。头文件内容如下:

// utility.h

#ifndef INCLUDE_UTILITY_
#define INCLUDE_UTILITY_

enum CalcType {
    ADD,
    MINUS,
    MULTIPLE,
    DIVIDE
};

class Utility {
public:
    int ArithmeticCalculation(CalcType op, int a, int b);

    double ArithmeticCalculation(CalcType op, double a, double b);

    bool IsLeapYear(int year);
};

#endif
这个头文件说明如下:

  • 头文件包含了三个函数,前两个用来做intdouble类型的四则运算。最后一个判断输入的年份是否是闰年。

  • 当然,在实际的工程中,前两个函数合并实现为一个泛型函数更为合适。但这里之所以分成两个,是为了查看代码覆盖率所用。

  • 关于

    闰年

    说明如下:

    • 能被4整除但不能被100整除的年份为普通闰年。
    • 能被100整除,也同时能被400整除的为世纪闰年。
    • 其他都不是闰年。
    • 例如:1997年不是闰年,2000年是闰年,2016年是闰年,2100不是闰年。
这三个函数的实现也不复杂:

// utility.cpp

#include "utility.h"

#include 
#include 


  • using namespace std;

    int Utility::ArithmeticCalculation(CalcType op, int a, int b) {
        if (op == ADD) {
            return a   b;
        } else if (op == MINUS) {
            return a - b;
        } else if (op == MULTIPLE) {
            return a * b;
        } else {
            if (b == 0) {
                cout << "CANNO Divided by 0" << endl;
                return std::numeric_limits::max();
            }
            return a / b;
        }
    }

    double Utility::ArithmeticCalculation(CalcType op, double a, double b) {
        if (op == ADD) {
            return a   b;
        } else if (op == MINUS) {
            return a - b;
        } else if (op == MULTIPLE) {
            return a * b;
        } else {
            if (b == 0) {
                cout << "CANNO Divided by 0" << endl;
                return std::numeric_limits::max();
            }
            return a / b;
        }
    }

    bool Utility::IsLeapYear(int year) {
        if (year % 100 == 0 
  • 21ic电子网

    扫描二维码,关注更多精彩内容

    本站声明: 本文章由作者或相关机构授权发布,目的在于传递更多信息,并不代表本站赞同其观点,本站亦不保证或承诺内容真实性等。需要转载请联系该专栏作者,如若文章内容侵犯您的权益,请及时联系本站删除。
    换一批
    延伸阅读

    2023年10月18日,中国在第三届“一带一路”国际合作高峰论坛期间发布《全球人工智能治理倡议》,围绕人工智能发展、安全、治理三方面系统阐述了人工智能治理中国方案。

    关键字: 人工智能 大模型 代码

    我们看到这么多的安全问题,部分原因在于我们对待安全的方式:安全性通常被认为是事后考虑的问题,是在开发结束时才添加到设备上的东西。然而,复杂的系统,尤其是嵌入式系统,有一个很大的攻击面,这让攻击者有机可乘,能够在“盔甲”上...

    关键字: 代码 嵌入式系统 软件漏洞

    新富人群财务需求多元发展,投顾服务迎来新机遇 上海2023年9月20日 /美通社/ -- 2023年9月19日,上海交通大学上海高级金融学院(高金)与全球领先的金融服务机构嘉信理财(Charles Schwab)联合发...

    关键字: BSP ADVANCED INA 代码

    北京2023年9月14日 /美通社/ -- 生物医药高科技公司诺诚健华(港交所代码:09969;上交所代码:688428)今日宣布,新型蛋白酪氨酸磷酸酶SHP2变构抑制剂ICP-189联用针对表皮生长因子受体(EGFR)...

    关键字: IC HP 代码 ARMA

    上海2023年9月1日 /美通社/ -- 2023上半年,安集科技(股票代码:688019)市场拓展规划成效显现,营业收入稳健增长。 全球半导体产业挑战持续存在的情形下,安集科技秉承发扬"克难攻坚,敢打硬...

    关键字: 安集科技 BSP 代码 半导体材料

    国际酒店运营商升级其在线支付功能 上海2023年8月28日 /美通社/ -- 加拿大金融科技公司Nuvei Corporation(以下简称“Nuvei”或“公司”)(纳斯达克代码:NVEI)(多伦多证券交易所代码:N...

    关键字: 代码 IP SE 纳斯达克

    2023年上半年收入7.459亿元 同比增长5.1% 毛利率水平上升 海外收入同比增长65.4% 香港2023年8月22日 /美通社/ -- 金邦达宝嘉控股有限公司及其附属公司(以下合称「金邦达」、「...

    关键字: 数字化 代码 嵌入式软件 COM

    我们经常对正在进行数字化转型的亚马逊云科技客户建议,将云迁移视为其数字化转型的一部分,数字化转型本身必须由业务成果驱动。其中治理计划的有效性决定了云迁移和数字化转型的成功与否。数字化转型中的云迁移总有结束的时候,但是如果...

    关键字: 代码 数字化 云服务

    广州及苏州生产基地产品均实现"出口"零突破 北京2023年8月21日 /美通社/ -- 百济神州(纳斯达克代码:BGNE;香港联交所代码:06160;上交所代码:688235)是一家全球性生物科技公...

    关键字: 神州 代码 TI PD

    近年来,国内电子公司和芯片设计企业大举进攻汽车、医疗和工业等高可靠应用(mission-critical)领域,为自己找到了摆脱红海的新领域。但是高可靠应用多数都需要功能安全认证,在许多行业在诸如汽车、航空电子、医疗和工...

    关键字: 代码 代码分析工具
    关闭
    关闭