1. Glib 单元测试框架

Glib 为单元测试提供了一套完整的测试框架,每个测试运行包括以下几个部分

  • 测试数据结构
  • 测试 setup 与 teardown 函数
  • 测试函数

2. 单元测试数据结构

在一组测试中使用的元素称为一个 fixture,Glib 要求每个 fixture 都是一个结构,所以我们需要声明一个结构体,包含我们测试数据或是对象。

/** fixture for Glib test */
typedef struct Matrix2dTest
{
Matrix2d mat; ///< test object
double TOL; ///< maximum error
} Matrix2dTest;

如上代码中定义了我们测试所需结构体 Matrix2dTest,结构体中元素 mat 即为后面进行测试的对象(结构体)。

3. 测试环境构建

在每个测试函数运行前,可能都需要进行一系列初始化和后处理操作。Glib 运行时将这两个过程函数作为参数传给测试函数,如此一来,每个测试函数都可以生成一个新的程序来运行,并且互不影响。

添加测试函数的语句是

g_test_add("/MatUtils/Test_inverseMatrix2d",  // test label
Matrix2dTest, // test structure
NULL, // input user data
Setup_Matrix2dTest, // setup function
Test_inverseMatrix2d, // test function
Teardown_Matrix2dTest); // teardown function

其中,Matrix2dTestNULL 为测试函数输入参数,Test_inverseMatrix2d 为测试函数,Setup_Matrix2dTestTeardown_Matrix2dTest 为测试初始化和后处理函数。

在调用测试函数时候,Glib 通过 fork 系统调用生成新的线程来测试失败。运行 g_test_run() 语句即可运行所有测试,具体执行命令可参见第5节。

4. 单元测试函数

由于测试框架是确定的,因此单元测试函数参数也是固定的。在上面测试中,我们添加测试函数名为 Test_inverseMatrix2d,函数完整定义为

static void
Test_inverseMatrix2d(Matrix2dTest* mattest, const void* test_data)
{
Matrix2d mat1;
mallocMatrix2d(3, 3, &mat1);
copyMatrix2d(mattest->mat, &mat1);
inverseMatrix2d(mat1);
Matrix2d mat2;
mallocMatrix2d(3, 3, &mat2);
multiplyMatrix2d(mattest->mat, mat1, 1.0, 0.0, mat2);
g_assert(fabs(mat2.ets[0][0] - 1.0) < mattest->TOL);
g_assert(fabs(mat2.ets[1][1] - 1.0) < mattest->TOL);
g_assert(fabs(mat2.ets[2][2] - 1.0) < mattest->TOL);
freeMatrix2d(mat1);
freeMatrix2d(mat2);
}

可以看到,由于函数只有文件内测试用,因此声明为 static 函数。函数有两个参数,第一个为 mattest 类型指针,第二个为传入用户数据,由于我们不需要其他数据,因此调用时传入 NULL

这个测试主要用来测试矩阵求逆过程,计算完成后将逆矩阵与矩阵相乘,看结果是否为单元矩阵。最后的判断采用如下语句

  g_assert(fabs(mat2.ets[0][0] - 1.0) < mattest->TOL);
g_assert(fabs(mat2.ets[1][1] - 1.0) < mattest->TOL);
g_assert(fabs(mat2.ets[2][2] - 1.0) < mattest->TOL);

5. 完整测试函数

#include <stdio.h>
#include <math.h>
#include "MatUtils.h"
#include <glib.h> /** fixture for Glib test */
typedef struct Matrix2dTest
{
Matrix2d mat; ///< test object
double TOL; ///< maximum error
} Matrix2dTest; static void
Setup_Matrix2dTest(Matrix2dTest* mattest, const void* test_data)
{
mallocMatrix2d(3, 3, &(mattest->mat));
double vt[] = {
3.0, -1.0, -1.0, 4.0, -2.0, -1.0, -3.0, 2.0, 1.0,
};
mattest->TOL = 1e-6;
setMatrix2dFromArray(vt, mattest->mat);
} static void
Teardown_Matrix2dTest(Matrix2dTest* mattest, const void* test_data)
{
freeMatrix2d(mattest->mat);
} static void
Test_inverseMatrix2d(Matrix2dTest* mattest, const void* test_data)
{
Matrix2d mat1;
mallocMatrix2d(3, 3, &mat1);
copyMatrix2d(mattest->mat, &mat1);
inverseMatrix2d(mat1);
Matrix2d mat2;
mallocMatrix2d(3, 3, &mat2);
multiplyMatrix2d(mattest->mat, mat1, 1.0, 0.0, mat2);
g_assert(fabs(mat2.ets[0][0] - 1.0) < mattest->TOL);
g_assert(fabs(mat2.ets[1][1] - 1.0) < mattest->TOL);
g_assert(fabs(mat2.ets[2][2] - 1.0) < mattest->TOL);
freeMatrix2d(mat1);
freeMatrix2d(mat2);
} int
main(int argc, char** argv)
{
g_test_init(&argc, &argv, NULL);
g_test_add("/MatUtils/Test_inverseMatrix2d", // test label
Matrix2dTest, // test structure
NULL, // input user data
Setup_Matrix2dTest, // setup function
Test_inverseMatrix2d, // test function
Teardown_Matrix2dTest); // teardown function
return g_test_run();
}

编译时由于需要链接 Glib 函数库所以需要添加一些附加命令。这里我采用的是 cmake 软件编译,在工程根目录的 CMakeLists.txt 中直接添加如下命令添加 Glib 头文件和库文件目录,

find_package(PkgConfig REQUIRED)
pkg_check_modules(GLIB REQUIRED glib-2.0>=2.23)
include_directories(${GLIB_INCLUDE_DIRS})
link_directories(${GLIB_LIBRARY_DIRS})

在测试函数所在的文件夹内 CMakeLists.txt 中,则添加如下命令编译测试函数

target_link_libraries(MatUtilsTest matutils ${GLIB_LIBRARIES})

如此便可对测试函数进行正确的编译和链接了。

获得可执行的测试函数后,用过命令

gtester -k -o=test.xml ./MatUtilsTest

生成测试结果文件 test.xml,再利用语句 gtester-report 命令即可将 XML 文件转化为 html 文件,并用留浏览器打开。

gtester-report test.xml > test.html

但是不知什么原因,在我的 Macbook 笔记本上出现错误

➜  build gtester-report test.xml > test.html
Traceback (most recent call last):
File "/usr/local/bin/gtester-report", line 490, in <module>
main()
File "/usr/local/bin/gtester-report", line 484, in main
HTMLReportWriter(rr.get_info(), rr.binary_list()).printout()
File "/usr/local/bin/gtester-report", line 348, in printout
self.handle_info ()
File "/usr/local/bin/gtester-report", line 242, in handle_info
self.oprint ('<h3>Package: %(package)s, version: %(version)s</h3>\n' % self.info)
KeyError: 'package'

通过搜索我发现是 xml 文件缺少内容所致 Running gtester-report on gtester output raises KeyError。打开 xml 文件,在 <gtester> 字段内添加

<info>
<package>PACKAGENAME</package>
<version>VERSION</version>
<revision>REVISION</revision>

语句,声明软件名和软件版本。如此便可得到可视化的测试结果,如图所示:

参考文献

理解 GLib 的单元测试框架

https://segmentfault.com/a/1190000003996312

Ben Klemens. C程序设计新思维[M]. 人民邮电出版社, 2015.

Glib 对 C 函数进行单元测试的更多相关文章

  1. 如何在单元测试中测试异步函数,block回调这种

    大概有四种方法: runloop 阻塞主进程等待结果 semphaore 阻塞主进程等待结果 使用XCTestExpectation 阻塞主线程等待(我用这个,xcode自带的,为啥不用) 使用第三方 ...

  2. 使用IdleTest进行TDD单元测试驱动开发演练(3) 之 ASP.NET MVC

    一.[前言] (1)本文将用到IOC框架Unity,可参照<Unity V3 初步使用 —— 为我的.NET项目从简单三层架构转到IOC做准备>(2)本文的解决方案是基于前述<使用I ...

  3. 【单元测试】NUint使用详解及Visual Studio配置

    阅读目录 什么是单元测试? 为什么使用单元测试? NUint使用详解: 示例 属性 断言 简单测试 VS配置: External Tools Visual Nunit 2010 NUnit Test ...

  4. The Hacker's Guide To Python 单元测试

    The Hacker's Guide To Python 单元测试 基本方式 python中提供了非常简单的单元测试方式,利用nose包中的nosetests命令可以实现简单的批量测试. 安装nose ...

  5. iOS单元测试(作用及入门提升)

    由于只是一些简单实用的东西,学学还是挺不错的.其实单元测试用的好,开发起来也会快很多.单元测试对于我目前来说,就是为了方便测试一些功能是否正常运行,还有调试接口是否能正常使用.有时候你可能是为了测试某 ...

  6. C++之单元测试

    以前编写程序从没有做过单元测试的工作,所以在后期会花很多时间去纠错,这也就是软件工程中的2:8定律.最近要完成一个项目,要求要对系统中的主类和主函数作出单元测试的保证,才去查找了相关方面的资料,看过后 ...

  7. 单元测试系列:如何使用JUnit+JaCoCo+EclEmma完成单元测试

    更多原创测试技术文章同步更新到微信公众号 :三国测,敬请扫码关注个人的微信号,感谢!   原文链接:http://www.cnblogs.com/zishi/p/6726664.html -----如 ...

  8. 【iOS】单元测试

    iOS单元测试(作用及入门提升) 字数1704 阅读16369 评论26 喜欢247 由于只是一些简单实用的东西,学学还是挺不错的.其实单元测试用的好,开发起来也会快很多.单元测试对于我目前来说,就是 ...

  9. python六十六课——单元测试(二)

    ''' 封装Person类 ''' class Person: def __init__(self,name,age): self.name=name self.age=age def getAge( ...

随机推荐

  1. 【数据结构与算法Python版学习笔记】引言

    学习来源 北京大学-数据结构与算法Python版 目标 了解计算机科学.程序设计和问题解决的基本概念 计算机科学是对问题本身.问题的解决.以及问题求解过程中得出的解决方案的研究.面对一 个特定问题,计 ...

  2. 软工博客之关于CSDN的移动端软件测评

    关于CSDN的移动端软件测评 项目 内容 这个作业属于哪个课程 2020春季计算机学院软件工程(罗杰 任健) 这个作业的要求在哪里 软件测评作业 我在这个课程的目标 不求变强,只求做好,成为一颗有用的 ...

  3. HDMI之TMDS通道

    HDMI标准继续沿用了和DVI相同的,由Silicon Image公司发明的TMDS(Time Minimized Differential Signal)最小化传输差分信号传输技术.TMDS是一种微 ...

  4. mysql 的安装方式

    一.rpm包安装方式 mysql-community-client-5.7.18-1.el7.x86_64.rpm 客户端 mysql-community-devel-5.7.18-1.el7.x86 ...

  5. Linux环境下安装中山大学东校区iNode客户端

    在中山大学登录校园网有两种方式,一种是连接WiFi,另一种是连接网线.这两种上网方式都需要用到NetID,但是连接网线的话还需要使用到iNode客户端(指东校区). Windows下iNode客户端的 ...

  6. sed tr 批量转换邮箱格式 去除"\n" 行尾添加";"

    1:从phpmyadmin上拿下来的数据是这样的: 2:od -c 发现存在\r\n (windows上编码问题) $ od -c sql.csv 先将\r处理掉 $ sed 's/\r//' sql ...

  7. 【python】以souhu邮箱为例学习DDT数据驱动测试

    前言 DDT(Data-Driven Tests)是针对 unittest 单元测试框架设计的扩展库.允许使用不同的测试数据来运行一个测试用例,并将其展示为多个测试用例.通俗理解为相同的测试脚本使用不 ...

  8. Python基础(偏函数)

    import functools#functools.partial就是帮助我们创建一个偏函数的,functools.partial的作用就是,把一个函数的某些参数给固定住(也就是设置默认值),返回一 ...

  9. .Net Core微服务——Ocelot(3):超时、熔断、限流

    基本概念 超时.熔断.限流听起来好像很远,但实际上用在方方面面.很多人可能还搞不懂熔断是做什么,其实可以把熔断理解为一种防护措施.做个假设,在微服务体系下,某个下游服务响应很慢,然后随着时间推移,会有 ...

  10. Docker部署 Mysql .Net6等容器

    Centos8安装Docker 1.更新一下yum [root@VM-24-9-centos ~]# yum -y update 2.安装containerd.io # centos8默认使用podm ...