单元测试

一、Google Test

Google Test(简称GTest) 是一个用于 C++ 编程语言的单元测试框架。它是由 Google 开发并开源的一个项目,旨在帮助开发者编写高质量的 C++ 代码。使用 Google Test,开发者可以轻松地为他们的 C++ 应用程序创建测试用例,确保代码的正确性和稳定性。

主要特点
  • 支持多种平台和编译器:Google Test 支持 Windows、Linux、MacOS 等多个平台和多种编译器,包括 GCC、Clang、MSVC 等
  • 丰富的断言:提供了一系列的断言宏来检查代码的正确性,例如 EXPECT_EQ 用于比较两个值是否相等。
  • 测试固定装置:允许定义一个测试环境(称为测试固定装置),在多个测试中重用这个环境,以减少重复代码。
  • 死亡测试:可以用来验证某些代码是否会如预期那样导致程序崩溃。
  • 性能测试:支持编写性能测试用例,可以测量代码的执行时间。
  • 易于集成:可以与各种构建系统(如 CMake、Bazel)和持续集成服务(如 Jenkins)集成。
使用步骤
  1. 下载 Google Test:可以从 Google Test的官方网站或 GitHub 仓库下载源代码。
  2. 编译 Google Test:将 Google Test源代码编译成静态库或直接包含源文件到你的项目中。
  3. 编写测试用例:使用 Google Test提供的宏和函数来定义测试用例。
  4. 运行测试:编译并运行包含测试用例的程序,查看测试结果。
示例代码

文件结构

root@z-VirtualBox:/disk3/gTest# tree
.
├── calculate_gtest.cc #测试用例,使用gTest测试函数功能
├── calculate.c #定义函数
├── calculate.h #声明函数
├── gtest #编译好的GTest
│   ├── include
│   └── lib
│       ├── libgtest.a
│       └── libgtest_main.a
└── libcalculate.so #使用g++编译出的动态库

待测试的代码: calculate.h、calcualte.c

//calculate.h
#ifndef __CALCULATE_H__
#define __CALCULATE_H__


#include <stdio.h>
#include <stdlib.h>

int add(int a, int b);
int sub(int a, int b);
int divide(int a,int b);

#endif


//calcualte.c
#include "calculate.h"

int add(int a,int b){
    return a+b;
}

int sub(int a, int b){
    return a-b;
}

测试用例: calcualte_gtest.cc

//calcualte_gtest.cc
#include "calculate.h"
#include <gtest/gtest.h>

TEST(calculateTest,addTest)
{
    EXPECT_EQ(88,add(80,8));
    EXPECT_EQ(0,add(1,-1));
}

TEST(calculateTest,subTest)
{
    EXPECT_EQ(8,sub(10,2));
    EXPECT_EQ(2,sub(1,-1));
}

编译测试用例

1、不使用libcalculate.so

g++ calculate.c calculate_gtest.cc -o a.out  -I. -Igtest/include -Lgtest/lib -lgtest -lgtest_main -lpthread

2、使用libcalculate.so

1)编译出ibcalculate.so

g++ -shared -fPIC -I ./ -o ./libcalculate.so calculate.c

2)链接到libcalculate.so

g++ calculate_gtest.cc -o a.out -I. -Igtest/include -Lgtest/lib -lgtest -lgtest_main -L. -lcalculate -lpthread

静态库、动态库的相关文章:C/C++静态库和动态库(共享库)

3.运行a.out

在这里插入图片描述

Google Test相关学习资料(学习如何编译得到GTest库,以及GTest的使用):

两步实现在C代码中快速集成gtset进行单元测试

嵌入式平台使用gtest进行白盒测试

玩转Google开源C++单元测试框架Google Test系列

感谢以上文章的作者。

二、CuTest

CuTest 是一个轻量级的 C 语言单元测试框架。它由 Neil Butterworth 开发,设计目的是为了简单易用,同时保持足够的功能来满足大多数 C 语言项目的测试需求。CuTest 的主要特点是其简洁性和易用性,使得开发者可以快速上手并开始编写测试用例。

主要特点
  • 简单易用:API 非常简单,容易学习和使用。
  • 轻量级:库文件非常小,不会给项目带来额外的负担。
  • 跨平台:可以在多种操作系统上运行,包括 Windows、Linux 和 macOS。
  • 文本输出:测试结果以文本形式输出,便于阅读和调试。
使用步骤
  1. 下载 CuTest:可以从 CuTest 的官方网站或 GitHub 仓库下载源代码。
  2. 编译 CuTest:将 CuTest 源代码编译成静态库或直接包含源文件到你的项目中。
  3. 编写测试用例:使用 CuTest 提供的宏和函数来定义测试用例。
  4. 运行测试:编译并运行包含测试用例的程序,查看测试结果。
示例代码

文件结构

root@z-VirtualBox:/disk3/cuTest# tree
.
├── calculate_cuTest.c #使用cuTest编写测试用例
├── calculate.c #同上
├── calculate.h #同上
├── cutest
│   ├── include
│   │   └── CuTest.h
│   └── lib
│       └── libcutest.a
└── libcalculate.so #使用gcc编译

测试用例:calculate_cuTest.c

//calculate_cuTest.c

/* 包含cutest头文件 */
#include "CuTest.h"
#include "calculate.h"

/**
 * @note
 *  定义测试用例xxx
 */
static void CuTestCase_0(CuTest *tc)
{
    /// 断言测试
    CuAssertIntEquals(tc, 2, add(1, 1));
}

static void CuTestCase_1(CuTest *tc)
{
    /// 断言测试
    CuAssertIntEquals(tc, 1 , sub(2,1));
}

/**
 * @note
 *  定义测试套件
 */
static CuSuite *CuSuite_x(void)
{
    CuSuite *suite = CuSuiteNew();

    /// 将测试用例添加到测试套件中
    SUITE_ADD_TEST(suite, CuTestCase_0);
    SUITE_ADD_TEST(suite, CuTestCase_1);

    return suite;
}

static void RunAllTests(void)
{
    CuSuite *suite = CuSuiteNew();
    CuString *output = CuStringNew();

    /// 声明测试套件,并增加测试用例
    CuSuiteAddSuite(suite, CuSuite_x());

    /// 运行所有测试用例
    CuSuiteRun(suite);
    
    /// 输出测试结果
    CuSuiteSummary(suite, output);
    CuSuiteDetails(suite, output);

    printf("\n\033[36m-----------------------------------------------------------------------------\n");
    printf("[%s] Test Result:\n", __FILE__);
    printf("%s\n", output->buffer);
    printf("\n[%s] End of Test Result.\n", __FILE__);
    printf("-----------------------------------------------------------------------------\033[0m\n\n");

    /// 释放测试资源
    CuStringDelete(output);
    CuSuiteDelete(suite);
}

int main(int argc, char *argv[])
{
    RunAllTests();
    return 0;
}

编译测试用例

1、不使用libcalculate.so

 gcc calculate.c calculate_cuTest.c  -o a.out -I. -Icutest/include -Lcutest/lib -lcutest -lpthread

2、使用libcalculate.so

1)编译出libcalculate.so

gcc -shared -fPIC -I ./ -o ./libcalculate.so calculate.c

2)链接到libcalculate.so

gcc calcualte_cuTest.c -o a.out -I. -Icutest/include -Lcutest/lib -lcutest  -L. -lcalculate -lpthread

3.运行a.out

在这里插入图片描述

代码覆盖率测试

代码覆盖率测试是一种评估软件测试质量的方法,通过测量测试用例执行时实际覆盖到的源代码的比例,来判断测试的充分性。高覆盖率通常意味着更多的代码路径得到了验证,从而提高了软件的可靠性和质量。常用的覆盖率指标包括:

  1. 行覆盖率(Line Coverage):已执行的代码行数占总代码行数的比例。
  2. 分支覆盖率(Branch Coverage):已执行的条件分支数占总条件分支数的比例。
  3. 函数覆盖率(Function Coverage):已调用的函数数占总函数数的比例。
  4. 语句覆盖率(Statement Coverage):已执行的语句数占总语句数的比例。
  5. 路径覆盖率(Path Coverage):已执行的路径数占总路径数的比例。

有许多工具可以帮助进行代码覆盖率测试,下面只介绍gcov和lcov

gcov:GNU Compiler Collection 提供的覆盖率测试工具,适用于 C 和 C++ 代码。

lcov:基于 gcov 的图形化前端,可以生成 HTML 报告。

GCOV的工作原理:编译器编译源代码时,将额外的代码注入到目标代码中,这些代码将用于记录每个源代码行被执行的次数。当程序运行时,gcov会生成一个.gcda文件,其中包含记录了代码执行信息的数据。然后,通过运行gcov命令来解析这个文件,并将其与源代码相匹配,生成一个HTML格式的测试报告,显示了每个源代码行被执行的次数、未被执行的代码行以及总体覆盖率等信息。

一、使用 gcov 和 lcov 进行代码覆盖率测试

安装工具

首先,确保你已经安装了 gcov 和 lcov。在大多数 Linux 发行版中,可以通过包管理器安装:

sudo apt-get install gcov lcov
编译代码

-g选项是为了生成调试信息,-fprofile-arcs选项是为了生成代码覆盖率统计信息

gcc -g -fprofile-arcs -ftest-coverage test.c -o test

这将生成一个名为test的可执行文件和一个名为test.gcno的文件

运行程序
./test

运行完程序后,在程序所在目录下会生成一个名为test.gcda的文件,这个文件记录了程序运行时每个代码块被执行的次数

生成覆盖率报告

使用 lcov 生成覆盖率数据文件,并生成 HTML 报告。

# 生成行覆盖率
lcov --capture --directory . --output-file test.info

# 生成分支覆盖率
lcov --capture --directory . --output-file test.info --rc lcov_branch_coverage=1

将在当前目录下生成一个名为test.info的文件,其中包含了测试覆盖率的数据。接着运行genhtml命令,生成HTML格式的测试覆盖率报告

genhtml test.info --output-directory out

这将生成一个名为out的目录,其中包含了各种统计信息和测试覆盖率报告。

查看报告

打开生成的index.html文件,查看测试覆盖率和未被覆盖到的代码行等信息

tip:通过以上步骤我们可以发现:lcov需要在gcov生成的数据文件的基础上进行操作,因此在使用lcov之前必须先运行测试用例并生成gcov数据文件。另外,lcov还支持一些高级的功能,例如过滤不需要统计的文件和代码行等,可以根据实际需要进行配置。

Logo

北京人形旗下天工造物具身智能开源社区,聚焦具身天工与慧思开物两大平台

更多推荐