JMing's Blog

C语言轻量级单元测试

字数统计: 1k阅读时长: 4 min
2024/04/02

minunit

简介

项目地址:https://github.com/siu/minunit

开源协议:MIT License

一个非常轻便的、便于使用的 C 语言单元测试框架。它没有任何依赖,所有功能都以宏函数的形式放在头文件中,开箱即用。该项目的 README 中是这样描述的:

Minunit 是一个最小的 C/C++ 单元测试框架,独立于单个头文件中。

它提供了一种定义和配置测试套件以及一些方便的断言类型的方法。它报告运行的测试数量、断言数量和经过的时间的摘要。

请注意,该项目基于: http://www.jera.com/techinfo/jtns/jtn002.html

该项目基于 John Brewer 的 “C 的最小单元测试框架” MinUnit。它只有三行,如下:

1
2
3
4
/* file: minunit.h */
#define mu_assert(message, test) do { if (!(test)) return message; } while (0)
#define mu_run_test(test) do { char *message = test(); tests_run++; if (message) return message; } while (0)
extern int tests_run;

这是一个极其简单的单元测试框架(或许不应该叫它框架),它只拥有简单的断言、运行测试用例、测试用例技术的功能。很显然,它的功能并不完善,在更大的项目中不便于使用,所以就有了本文中介绍的,更加完善,同时也保持它的轻便的 MinUnit。

使用

引入 MinUnit

直接包含头文件即可。

1
2
3
#include "minunit.h"
// ...
int main() { // ... }

断言

mu_check(condition): 检测传入的布尔表达式是否为真,如果不为真则打印错误信息。

mu_fail(message): 手动出错,打印信息。(计入断言数量)

mu_assert(test, message): 在 mu_check() 的基础上加上失败打印自定义信息。

mu_assert_int_eq(expected, result): 检测返回值是否与期望值相等(整型)。

mu_assert_string_eq(expected, result): 检测返回值是否与期望值相等(字符串)。

mu_assert_double_eq(expected, result): 检测返回值是否与期望值近似(浮点型),精度阈值通过在头文件中定义的宏 MINUNIT_EPSILON 来控制。

判断两个浮点数是否近似:两数之差的绝对值与阈值 EPSILON 做比较,如果它小于 EPSILON 则两数近似。

测试用例和测试组合

我们可以用 MU_TEST() 定义一个测试用例。

1
MU_TEST(test_case1) { // ... }

实际上,上面的代码会被展开为这样:

1
static void test_case1(void) {}

使用 MU_RUN_TEST() 运行测试用例。

1
2
3
4
5
6
#include "minunit.h"
MU_TEST(test_case1) { mu_check(5 == 7); }
int main() {
MU_RUN_TEST(test_case1);
return 0;
}

测试组合可以用 MU_TEST_SUITE() 定义,一个测试组合可以将多个测试用例组合起来(其实就是一个函数里面调用了一堆函数)。

1
2
3
4
5
6
7
MU_TEST(test_case1) { mu_check(5 == 7); }
MU_TEST(test_case2) { mu_check(5 != 7); }

MU_TEST_SUITE(test_suite1) {
MU_RUN_TEST(test_case1);
MU_RUN_TEST(test_case2);
}

使用 MU_RUN_SUITE() 运行测试组合。

1
2
3
4
5
#include "minunit.h"
int main() {
MU_RUN_SUITE(test_suite1);
return 0;
}

装载与卸载函数

我们可以通过在测试组合中使用 MU_SUITE_CONFIGURE() 来绑定装载函数与卸载函数。装载函数和卸载函数会在运行每一个测试用例之前和之后运行,用于运行测试用例的准备工作和善后工作。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include "minunit.h"

void test_setup(void) { printf("test_setup\n"); }

void test_teardown(void) { printf("test_teardown\n"); }

MU_TEST(test_case1) { mu_check(5 != 7); }
MU_TEST(test_case2) { mu_check(6 == 7); }

MU_TEST_SUITE(test_suite1) {
MU_SUITE_CONFIGURE(&test_setup, &test_teardown);
MU_RUN_TEST(test_case1);
MU_RUN_TEST(test_case2);
}

int main() {
MU_RUN_SUITE(test_suite1);
MU_REPORT();
return 0;
}

以上代码在一个测试组合中运行了两个测试用例,并且绑定了装载函数和卸载函数。这两个函数会在运行每个测试用例前后运行。

测试报告

通过 MU_REPORT() 打印当前为止的测试结果报告,他将打印测试用例的个数、断言的个数和失败的个数,并计算测试完成的真实时间与处理器时间。以上代码运行结果如下:

1
2
3
4
5
6
7
8
9
10
11
12
test_setup
.test_teardown
test_setup
F
test_case2 failed:
/Users/jm/CLionProjects/data_structure/test/llist/llist_test.c:144: 6 == 7
test_teardown


2 tests, 2 assertions, 1 failures

Finished in 0.00004292 seconds (real) 0.00000800 seconds (proc)

END,可以愉快使用了!

CATALOG
  1. 1. minunit
    1. 1.1. 简介
    2. 1.2. 使用
      1. 1.2.1. 引入 MinUnit
      2. 1.2.2. 断言
      3. 1.2.3. 测试用例和测试组合
      4. 1.2.4. 装载与卸载函数
      5. 1.2.5. 测试报告
    3. 1.3. END,可以愉快使用了!