Glib 对 C 函数进行单元测试
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
其中,Matrix2dTest 和 NULL 为测试函数输入参数,Test_inverseMatrix2d 为测试函数,Setup_Matrix2dTest 和 Teardown_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>
语句,声明软件名和软件版本。如此便可得到可视化的测试结果,如图所示:

参考文献
https://segmentfault.com/a/1190000003996312
Ben Klemens. C程序设计新思维[M]. 人民邮电出版社, 2015.
Glib 对 C 函数进行单元测试的更多相关文章
- 如何在单元测试中测试异步函数,block回调这种
		
大概有四种方法: runloop 阻塞主进程等待结果 semphaore 阻塞主进程等待结果 使用XCTestExpectation 阻塞主线程等待(我用这个,xcode自带的,为啥不用) 使用第三方 ...
 - 使用IdleTest进行TDD单元测试驱动开发演练(3) 之 ASP.NET MVC
		
一.[前言] (1)本文将用到IOC框架Unity,可参照<Unity V3 初步使用 —— 为我的.NET项目从简单三层架构转到IOC做准备>(2)本文的解决方案是基于前述<使用I ...
 - 【单元测试】NUint使用详解及Visual Studio配置
		
阅读目录 什么是单元测试? 为什么使用单元测试? NUint使用详解: 示例 属性 断言 简单测试 VS配置: External Tools Visual Nunit 2010 NUnit Test ...
 - The Hacker's Guide To Python 单元测试
		
The Hacker's Guide To Python 单元测试 基本方式 python中提供了非常简单的单元测试方式,利用nose包中的nosetests命令可以实现简单的批量测试. 安装nose ...
 - iOS单元测试(作用及入门提升)
		
由于只是一些简单实用的东西,学学还是挺不错的.其实单元测试用的好,开发起来也会快很多.单元测试对于我目前来说,就是为了方便测试一些功能是否正常运行,还有调试接口是否能正常使用.有时候你可能是为了测试某 ...
 - C++之单元测试
		
以前编写程序从没有做过单元测试的工作,所以在后期会花很多时间去纠错,这也就是软件工程中的2:8定律.最近要完成一个项目,要求要对系统中的主类和主函数作出单元测试的保证,才去查找了相关方面的资料,看过后 ...
 - 单元测试系列:如何使用JUnit+JaCoCo+EclEmma完成单元测试
		
更多原创测试技术文章同步更新到微信公众号 :三国测,敬请扫码关注个人的微信号,感谢! 原文链接:http://www.cnblogs.com/zishi/p/6726664.html -----如 ...
 - 【iOS】单元测试
		
iOS单元测试(作用及入门提升) 字数1704 阅读16369 评论26 喜欢247 由于只是一些简单实用的东西,学学还是挺不错的.其实单元测试用的好,开发起来也会快很多.单元测试对于我目前来说,就是 ...
 - python六十六课——单元测试(二)
		
''' 封装Person类 ''' class Person: def __init__(self,name,age): self.name=name self.age=age def getAge( ...
 
随机推荐
- Codeforces Round #747 (Div. 2) Editorial
			
Codeforces Round #747 (Div. 2) A. Consecutive Sum Riddle 思路分析: 一开始想起了那个公式\(l + (l + 1) + - + (r − 1) ...
 - the Agiles Scrum Meeting 4
			
会议时间:2020.4.12 20:00 1.每个人的工作 今天已完成的工作 yjy:基本完成广播功能,修复bug issues:小组任务1-增量开发组 Bug:冲刺 wjx:继续实现注销功能的后端 ...
 - Pogo-Cow S
			
这题出在单调队列优化dp里,就离谱好吧...... 对不住了上来先喷一波,不过离谱是确实的 dp的含义也很简单,就是说从j到i的分数最大值 直接上代马,里面说的很详细了 1 #include<b ...
 - Windows平台编译器相关的几个预定义宏
			
WIN32 是在windows.h 中定义的宏,包含winodws.h则定义该宏 _WIN32/_WIN64跟windows平台有关的宏,_WIN32在windows 32位和64位下都有该宏,_ ...
 - 中文NER的那些事儿4. 数据增强在NER的尝试
			
这一章我们不聊模型来聊聊数据,解决实际问题时90%的时间其实都是在和数据作斗争,于是无标注,弱标注,少标注,半标注对应的各类解决方案可谓是百花齐放.在第二章我们也尝试通过多目标对抗学习的方式引入额外的 ...
 - python3  调用 centos 常用系统命令
			
一.创建目录 1 import os 2 3 base_path = '/data/sw_backup' 4 addr= 'FT' 5 ip='192.168.1.1' 6 path = base_p ...
 - 面试题系列:用了这么多年的 Java 泛型,我竟然只知道它的皮毛
			
面试题:说说你对泛型的理解? 面试考察点 考察目的:了解求职者对于Java基础知识的掌握程度. 考察范围:工作1-3年的Java程序员. 背景知识 Java中的泛型,是JDK5引入的一个新特性. 它主 ...
 - Vue&Element开发框架中增加工作流处理,查看申请单中整合多个处理类型的处理
			
关于我在Winform框架.混合框架.Bootstrap开发框架中的简易审批性工作流模块,我写过不少文章,有兴趣可以参考<工作流模块>的随笔进行了解,本篇随笔在完成了Vue&Ele ...
 - Linux系统编程之进程概念
			
注:本文部分图片来源于网络,如有侵权,请告知删除 1. 什么是进程? 在了解进程概念之前,我们需要先知道程序的概念. 程序,是指编译好的二进制文件,这些文件在磁盘上,并不占用系统资源. 进程,指的是一 ...
 - Typecho 反序列化漏洞 分析及复现
			
0x00 漏洞简介 CVE-2018-18753 漏洞概述: typecho 是一款非常简洁快速博客 CMS,前台 install.php 文件存在反序列化漏洞,通过构造的反序列化字符串注入可以执行任 ...