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. 安装pytorch后import torch显示no module named 'torch'

    问题描述:在pycharm终端里通过pip指令安装pytorch,显示成功安装但是python程序和终端都无法使用pytorch,显示no module named 'torch'. 起因:电脑里有多 ...

  2. 四万字32图,Kafka知识体系保姆级教程宝典

    本文目录: 一.消息队列 Apache Pulsar Pulsar 与 Kafka 对比 二.Kafka基础 三.Kafka架构及组件 四.Kafka集群操作 五.Kafka的JavaAPI操作 六. ...

  3. 单片机stm32 USART串口实际应用解析

    stm32作为现在嵌入式物联网单片机行业中经常要用多的技术,相信大家都有所接触,今天这篇就给大家详细的分析下有关于stm32的出口,还不是很清楚的朋友要注意看看了哦,在最后还会为大家分享有些关于stm ...

  4. oeasy教您玩转vim - 56 - # 字符可视化模式

    ​ 可视化编辑 回忆上节课内容 我们学习了关于模式匹配中使用参数 单个参数 :%s/<h2>\(.*\)</h2>/ - \1/g 多个参数 :%s/<img src=\ ...

  5. go defer、return的执行顺序

    一.一个函数中多个defer的执行顺序 defer 的作用就是把defer关键字之后的函数执行压入一个栈中延迟执行,多个defer的执行顺序是后进先出LIFO,也就是先执行最后一个defer,最后执行 ...

  6. Swift-技巧(二)模糊脸部功能

    摘要 本文介绍模糊脸部的功能逻辑和实现方式,实现方式会尽可能的使用苹果提供的 API,保证功能高效率和简洁. 逻辑 模糊脸部的逻辑主要有两个流程,就是先找到脸部,然后模糊脸部,那么就引申出这两个实现问 ...

  7. 『学了就忘』Linux基础命令 — 26、帮助命令

    目录 1.man命令 (1)man命令的快捷键 (2)man命令的帮助级别(了解即可) (3)man命令的使用 2.info命令 3.help命令 4.--help选项 1.man命令 man是最常见 ...

  8. Redis核心原理与实践--事务实践与源码分析

    Redis支持事务机制,但Redis的事务机制与传统关系型数据库的事务机制并不相同. Redis事务的本质是一组命令的集合(命令队列).事务可以一次执行多个命令,并提供以下保证: (1)事务中的所有命 ...

  9. Go语言核心36讲(Go语言实战与应用一)--学习笔记

    23 | 测试的基本规则和流程 (上) 在接下来的日子里,我将带你去学习在 Go 语言编程进阶的道路上,必须掌握的附加知识,比如:Go 程序测试.程序监测,以及 Go 语言标准库中各种常用代码包的正确 ...

  10. SpringCloud升级之路2020.0.x版-34.验证重试配置正确性(2)

    本系列代码地址:https://github.com/JoJoTec/spring-cloud-parent 我们继续上一节针对我们的重试进行测试 验证针对限流器异常的重试正确 通过系列前面的源码分析 ...