C 语言常用单元测试框架

C 语言常用单元测试框架

1. C 单元测试框架

1.1 什么是单元测试和单元测试框架

单元测试是软件测试重要步骤,是对软件中最小可测试单元,在与其他部分隔离情况下, 进行检查校验,查看单元是否按设计意图工作。程序单元是应用的最小可测试部件。在过程化编程中,一个单元就是单个程序、函数、过程等;对于面向对象编程,最小单元就是方法,包括基类(超类)、抽象类、或者派生类(子类)中的方法[1]。举个例子:测试算术加法函数,为了确保计算功能正常,传入不同参数值,比较返回值,这个就是单元测试。

对每个软件模块执行单元测试,可以及时发现问题,降低后期检测、识别和修正bug成本。并且写一套全面的单元测试用例,会让用户自觉去考虑函数输入输出,错误情况,选择合理地组织架构。完善单元测试可以给予集成测试信心,在开发框架更新迭代,复杂系统的集成后,可以有效控制系统熵增。单元测试帮助适用者了解接口功能和适用方法。

单元测试也有局限,难以发现集成问题或者板载系统问题,因为难以遍历所有执行路径和输入条件,详细或者甚至“臃肿”的测试用例设计客观增加了代码工作量(考虑到小团队,编码人员本身又是测试人员)。另一方面,单元测试需要版本控制,记录修改内容,在单元测试失败可以比对新老版本,方便程序修改。而且,要想单元测试持续发挥作用,单元测试需要集成到团队工作流程中,规律审阅和及时修改,保持测试和程序更新同步

单元测试框架提供了一种统一的编程模型,可以将测试定义为一些简单的类,这些类中的方法可以调用希望测试的应用程序代码。开发人员不需要编写自己的测试控制工具。它可以提供用例组织与执行、提供丰富的断言方法、提供丰富的日志,总得来说,帮助我们更好的完成自动化测试。

1.2 常见单元测试框架

ShiYanLou/常用C语言单元测试工具介绍.md at master · Ewenwan/ShiYanLou (github.com) 中介绍了C 语言单元测试常用工具。特别针对嵌入式系统,下面几种单元测试框架更为适用 [^1]。

  • CUnit - A unit testing framework for C. Best suited for PC software
  • EmbUnit -A unit testing framework for embedded systems. Best suited for systems with embedded Linux
  • MinUnit - A minimalistic unit testing framework for C

1.3 引用

  1. Unit testing - Wikipedia
  2. 单元测试框架-为什么要学习单元测试框架以及单元测试框架的作用

2. MinUnit 单元测试框架简介

对于资源有限的环境,比如 C 语言嵌入式系统,常用的单元测试框架会带来大量的资源消耗,并且嵌入式更多侧重功能测试,完善的框架实现并非必要。MinUnit 使用 C 语言编写,非常小型的单元测试框架。编译时未引用头文件中定义的宏函数,并不占会用内存。

Unit testing frameworks are quite popular in the object-oriented programming world. Frameworks like JUnit (for Java), SUnit (for Smalltalk), and CppUnit (for C++) provide a rich set of functionality. However, this rich set of functionality can be intimidating to someone who wants to do unit testing in a more constrained environment, such as an embedded system written in C. But the important thing about unit testing is the testing, not the framework. MinUnit is an extremely simple unit testing framework written in C. It uses no memory allocation, so it should work fine under almost any circumstance, including ROMable code.

                                                                                                      — 摘自[JTN002 — Jera Design LLC](https://jera.com/techinfo/jtns/jtn002)

2.1 简化版本组成

测试框架只有三行,有断言和

/* 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; 

说明:

  1. 第一行断言,当测试条件不满足时,返回错误信息;
  2. 第二行运行测试,传入参数为函数指针,函数中无传入参数,当调用一次,全局变量测试次数 tests_run 自加 1。当返回的字符串不为空时,则返回 messgage
  3. 第三行外部定义(extern)的运行次数,通知编译器在外部寻找 tests_run 变量。

注意:JTN002 — Jera Design LLC 中有提到,头文件中为什么使用 do{} while 结构?

Question 10.4 (c-faq.com) 中有回答,宏中有多个声明时,为了防止替换后引入多个 “;”

#define MACRO(arg1, arg2) {stmt1; stmt2;}

// 在 if 条件语句分支调用 MACRO
if(cond)
		MACRO(arg1, arg2);
	else	/* some other code */

// 展开成
if(cond)
		{stmt1; stmt2;}; // 引入多个 “;”,造成编译出错
	else	/* some other code */

常规有两种解决方法:

第一种,使用 do{} while 结构,注意 while 后面没有 “;”

#define MACRO(arg1, arg2) do {	\
		/* declarations */	\
		stmt1;			\
		stmt2;			\
		/* ... */		\
		} while(0)	/* (no trailing ; ) */

第二种,使用一个括起来的表达式,里面包含一个或多个逗号操作符:

#define FUNC(arg1, arg2) (expr1, expr2, expr3)

2.2 简化版本范例

关于简化版本的使用,可以参考 minunit_example.c

/* file minunit_example.c */
 
 #include <stdio.h>
 #include "minunit.h"
 
 int tests_run = 0;
 
 int foo = 7;
 int bar = 4;
 
 static char * test_foo() {
     mu_assert("error, foo != 7", foo == 7);
     return 0;
 } //定义 test_foo 函数指针
 
 static char * test_bar() {
     mu_assert("error, bar != 5", bar == 5);
     return 0;
 } //定义 test_bar 函数指针
 
 static char * all_tests() {
     mu_run_test(test_foo);
     mu_run_test(test_bar);
     return 0;
 } // 组成测试组
 
 int main(int argc, char **argv) {
     char *result = all_tests();
     if (result != 0) {
         printf("%s\n", result);
     }
     else {
         printf("ALL TESTS PASSED\n");
     } // 获取测试结果,打印测试结果
     printf("Tests run: %d\n", tests_run); // 打印测试集个数
 
     return result != 0;
 }

该部分可以从这里下载测试

2.3 完善版本示例

https://github.com/siu/minunit

It provides a way to define and configure test suites and a few handy assertion types. It reports the summary of the number of tests run, number of assertions and time elapsed.

基于简化版本,该项目添加了常用功能,比如测试组、常见类型断言、报告测试数目、断言数目和耗时。

#include "minunit.h"

static int foo = 0;
static int bar = 0;
static double dbar = 0.1;
static const char* foostring = "Thisstring";

void test_setup(void) {
	foo = 7;
	bar = 4;
}

void test_teardown(void) {
	/* Nothing */
}

// #define MU_TEST(method_name) static void method_name(void)
// MU_TEST 只对 test_check 进行修饰, , 相当于 static void test_check(void) {}
MU_TEST(test_check) {
	mu_check(foo == 7); // 检查表达式正确与否,正确打印 “.”,错误打印所在函数、文件名、行号和测试条目名称。不论表达式正确与否,minunit_assert 均自加。
} // MU_TEST 只对方法名进行修饰

MU_TEST(test_check_fail) {
	mu_check(foo != 7);
} 

// 断言,基本和 mu_check 类似,会额外在表达式错误时,打印出断言 message 信息
// 举例:minunit_example.c:30: foo should be <> 7
MU_TEST(test_assert) {
	mu_assert(foo == 7, "foo should be 7"); 
} 

MU_TEST(test_assert_fail) {
	mu_assert(foo != 7, "foo should be <> 7");
}

// 整型值比较,不相等输出相关信息
// 举例:minunit_example.c:38: 5 expected but was 4
MU_TEST(test_assert_int_eq) {
	mu_assert_int_eq(4, bar);
} 

MU_TEST(test_assert_int_eq_fail) {
	mu_assert_int_eq(5, bar);
}

// Double 型比较,不相等输出相关信息
// 举例:minunit_example.c:46: 0.2 expected but was 0.1
MU_TEST(test_assert_double_eq) {
	mu_assert_double_eq(0.1, dbar);
}

MU_TEST(test_assert_double_eq_fail) {
	mu_assert_double_eq(0.2, dbar);
}

// 单纯输出错误信息
// 举例:minunit_example.c:50: Fail now!
MU_TEST(test_fail) {
	mu_fail("Fail now!");
}

// String 型比较,不相等输出相关信息
// 举例:minunit_example.c:58: 'Thatstring' expected but was 'Thisstring'
MU_TEST(test_string_eq){
	mu_assert_string_eq("Thisstring", foostring);
}

MU_TEST(test_string_eq_fail){
	mu_assert_string_eq("Thatstring", foostring);
}

// #define MU_TEST_SUITE(suite_name) static void suite_name(void)
// MU_TEST_SUITE 只对 test_suite 进行修饰, 相当于 static void test_suite(void) {}
MU_TEST_SUITE(test_suite) {
	
	// 配置 setup(准备好测试组所需参数等上下文条件)和 teardown 函数
	// 传入函数指针
	MU_SUITE_CONFIGURE(&test_setup, &test_teardown);
	
	// MU_RUN_TEST 主要初始化计时器、setup 、单元测试状态、运行测试、计数、打印信息以及解构。
	MU_RUN_TEST(test_check);
	MU_RUN_TEST(test_assert);
	MU_RUN_TEST(test_assert_int_eq);
	MU_RUN_TEST(test_assert_double_eq);

	MU_RUN_TEST(test_check_fail);
	MU_RUN_TEST(test_assert_fail);
	MU_RUN_TEST(test_assert_int_eq_fail);
	MU_RUN_TEST(test_assert_double_eq_fail);
	
	MU_RUN_TEST(test_string_eq);
	MU_RUN_TEST(test_string_eq_fail);

	MU_RUN_TEST(test_fail);
} 

int main(int argc, char *argv[]) {
	// 运行测试组,运行完毕后,取消 setup 和 teardown 函数配置
	/* #define MU_RUN_SUITE(suite_name) MU__SAFE_BLOCK(\
		suite_name();\
		minunit_setup = NULL;\
		minunit_teardown = NULL;\
	)*/
	MU_RUN_SUITE(test_suite);
	
	// 报告测试结果,包括运行数、断言数、失效数;实际时间和进程时间
	MU_REPORT();

	// #define MU_EXIT_CODE minunit_fail, 退出标志
	return MU_EXIT_CODE;
}

输出结果:

....F
test_check_fail failed:
        minunit_example.c:22: foo != 7
F
test_assert_fail failed:
        minunit_example.c:30: foo should be <> 7
F
test_assert_int_eq_fail failed:
        minunit_example.c:38: 5 expected but was 4
F
test_assert_double_eq_fail failed:
        minunit_example.c:46: 0.2 expected but was 0.1
.F
test_string_eq_fail failed:
        minunit_example.c:58: 'Thatstring' expected but was 'Thisstring'
F
test_fail failed:
        minunit_example.c:50: Fail now!

11 tests, 11 assertions, 6 failures

Finished in 0.00010750 seconds (real) 0.00010600 seconds (proc)

2.3 引用

  1. uCUnit: Main Page Unit Test Framework for Microcontrollers
  2. http://www.jera.com/techinfo/jtns/jtn002.html

3. CUnit 单元测试框架简介

CUnit 是一个编写、管理和运行 C 语言单元测试架构,会通过静态库链接在用户编写的测试代码中。CUnit 具有测试框架简洁、支持丰富数据类型断言、并提供多种运行测试和报告测试结果接口。接口包括自动化接口(Automated interface )和交互式接口(Interactive interface),其中交互式有控制窗和图形窗两种方式)。[^2]

  • 自动化接口 - Automated interface

    The screenshots below were generated by this code using the CUnit Automated  interface.
    The screenshots below were generated by this code using the CUnit Automated  interface.

    The screenshots below were generated by this code using the CUnit Automated
     interface.

  • 交互式接口-Interactive interface()

    The screenshots below were generated by this code using the CUnit Curses interface on Linux.
    The screenshots below were generated by this code using the CUnit Curses interface on Linux.

    The screenshots below were generated by this code using the CUnit Curses interface on Linux.

3.1 结构和常规用法

3.1.1 常用的数据类型和函数在头文件说明

Header File Description
#include http://cunit.sourceforge.net/doc/headers/CUnit.h ASSERT macros for use in test cases, and includes other framework headers.
#include http://cunit.sourceforge.net/doc/headers/CUError.h Error handing functions and data types. Included automatically by CUnit.h.
#include http://cunit.sourceforge.net/doc/headers/TestDB.h Data type definitions and manipulation functions for the test registry, suites, and tests. Included automatically by CUnit.h.
#include http://cunit.sourceforge.net/doc/headers/TestRun.h Data type definitions and functions for running tests and retrieving results. Included automatically by CUnit.h.
#include http://cunit.sourceforge.net/doc/headers/Automated.h Automated interface with xml output.
#include http://cunit.sourceforge.net/doc/headers/Basic.h Basic interface with non-interactive output to stdout.
#include http://cunit.sourceforge.net/doc/headers/Console.h Interactive console interface.
#include http://cunit.sourceforge.net/doc/headers/CUCurses.h Interactive console interface (*nix).
#include http://cunit.sourceforge.net/doc/headers/Win.h Windows interface (not yet implemented).

3.1.2 CUnit 结构

CUnit 的框架组织和常见单元测试框架一致,主要有 test registry、test suites 和 test cases 组成,框架组织如下:

                      Test Registry
                            |
             ------------------------------
             |                            |
          Suite '1'      . . . .       Suite 'N'
             |                            |
       ---------------             ---------------
       |             |             |             |
    Test '11' ... Test '1M'     Test 'N1' ... Test 'NM'

Test Cases 会被打包进 Test Suites,Test Suites 注册进 Test Registry 中。Test Suites 运行前有 setup 函数,准备同样的上下文环境 ,运行后调用 teardown 函数恢复初始状态。Test Registry 中测试工作放在单个函数中执行。

3.1.3 常规用法 — 典型流程

  1. 写测试函数 Write functions for tests (and suite init/cleanup if necessary).
  2. 初始化测试注册表 Initialize the test registry - CU_initialize_registry()
  3. 将测试组添加到测试注册表中 Add suites to the test registry - CU_add_suite()
  4. 将测试函数添加到测试组中 Add tests to the suites - CU_add_test()
  5. 用合适的接口运行测试 Run tests using an appropriate interface, e.g. CU_console_run_tests
  6. 清除测试注册表Cleanup the test registry - CU_cleanup_registry

该部分主要翻译自 CUnit 用户手册[^3]。

3.2 安装和测试

Linux 安装请参考 linux下的Cunit的编译与安装 (测试可行)和 https://github.com/jacklicn/CUnit

Winows 安装请参考 Windows 和 linux 下CUnit编译安装教程(未测试,但是目前找到最全面的)

STM32 嵌入式平台移植请参考 Cunit在嵌入式平台上的移植(STM32)

在不同平台上输出大同小异,这里编译测试在 Ubuntu20.04 环境下,参考上文 Linux 系统下安装,CUnit 安装在 /opt/Cunit。

// 
// 头文件存放在 /opt/Cunit/include/CUnit/ 路径下
// 库文件存放在 /opt/Cunit/lib/ 路径下
❯❯ Cunit  20:14 pwd
/opt/Cunit
❯❯ Cunit  20:15 ls -la
total 24
drwxr-xr-x 6 root root 4096 Apr  6 12:39 .
drwxr-xr-x 4 root root 4096 Apr  6 12:39 ..
drwxr-xr-x 3 root root 4096 Apr  6 12:39 doc
drwxr-xr-x 3 root root 4096 Apr  6 12:39 include
drwxr-xr-x 3 root root 4096 Apr  6 12:39 lib
drwxr-xr-x 4 root root 4096 Apr  6 12:39 share
❯❯ Cunit  20:15 ls -la include/CUnit/
total 132
drwxr-xr-x 2 root root  4096 Apr  6 12:39 .
drwxr-xr-x 3 root root  4096 Apr  6 12:39 ..
-rw-r--r-- 1 root root  3249 Apr  6 12:39 Automated.h
-rw-r--r-- 1 root root  3968 Apr  6 12:39 Basic.h
-rw-r--r-- 1 root root  7733 Apr  6 12:39 CUError.h
-rw-r--r-- 1 root root 19144 Apr  6 12:39 CUnit.h
-rw-r--r-- 1 root root  1707 Apr  6 12:39 CUnit_intl.h
-rw-r--r-- 1 root root  1768 Apr  6 12:39 Console.h
-rw-r--r-- 1 root root  4087 Apr  6 12:39 MyMem.h
-rw-r--r-- 1 root root 42460 Apr  6 12:39 TestDB.h
-rw-r--r-- 1 root root 22526 Apr  6 12:39 TestRun.h
-rw-r--r-- 1 root root  5959 Apr  6 12:39 Util.h
❯❯ Cunit   20:15 ls -la lib/
total 228
drwxr-xr-x 3 root root   4096 Apr  6 12:39 .
drwxr-xr-x 6 root root   4096 Apr  6 12:39 ..
-rw-r--r-- 1 root root 125770 Apr  6 12:39 libcunit.a
-rwxr-xr-x 1 root root    998 Apr  6 12:39 libcunit.la
lrwxrwxrwx 1 root root     17 Apr  6 12:39 libcunit.so -> libcunit.so.1.0.1
lrwxrwxrwx 1 root root     17 Apr  6 12:39 libcunit.so.1 -> libcunit.so.1.0.1
-rwxr-xr-x 1 root root  87064 Apr  6 12:39 libcunit.so.1.0.1
drwxr-xr-x 2 root root   4096 Apr  6 12:39 pkgconfig

测试文件为下载解压好的 /home/username/Desktop/CUnit-2.1-3/Examples/BasicTest/BasicTest.c 。

//BasicTest.c
/*
 *  CUnit - A Unit testing framework library for C.
 *  Copyright (C) 2004  Anil Kumar, Jerry St.Clair
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Library General Public
 *  License as published by the Free Software Foundation; either
 *  version 2 of the License, or (at your option) any later version.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Library General Public License for more details.
 *
 *  You should have received a copy of the GNU Library General Public
 *  License along with this library; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

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

#include "Basic.h"
#include "ExampleTests.h"

int main(int argc, char* argv[])
{
  CU_BasicRunMode mode = CU_BRM_VERBOSE;
  CU_ErrorAction error_action = CUEA_IGNORE;
  int i;

  setvbuf(stdout, NULL, _IONBF, 0);

  for (i=1 ; i<argc ; i++) {
    if (!strcmp("-i", argv[i])) {
      error_action = CUEA_IGNORE;
    }
    else if (!strcmp("-f", argv[i])) {
      error_action = CUEA_FAIL;
    }
    else if (!strcmp("-A", argv[i])) {
      error_action = CUEA_ABORT;
    }
    else if (!strcmp("-s", argv[i])) {
      mode = CU_BRM_SILENT;
    }
    else if (!strcmp("-n", argv[i])) {
      mode = CU_BRM_NORMAL;
    }
    else if (!strcmp("-v", argv[i])) {
      mode = CU_BRM_VERBOSE;
    }
    else if (!strcmp("-e", argv[i])) {
      print_example_results();
      return 0;
    }
    else {
      printf("\nUsage:  BasicTest [options]\n\n"
               "Options:   -i   ignore framework errors [default].\n"
               "           -f   fail on framework error.\n"
               "           -A   abort on framework error.\n\n"
               "           -s   silent mode - no output to screen.\n"
               "           -n   normal mode - standard output to screen.\n"
               "           -v   verbose mode - max output to screen [default].\n\n"
               "           -e   print expected test results and exit.\n"
               "           -h   print this message and exit.\n\n");
      return 0;
    }
  }

  if (CU_initialize_registry()) {
    printf("\nInitialization of Test Registry failed.");
  }
  else {
    AddTests();
    CU_basic_set_mode(mode);
    CU_set_error_action(error_action);
    printf("\nTests completed with return value %d.\n", CU_basic_run_tests());
    CU_cleanup_registry();
  }

  return 0;
}

编译 BasicTest

❯❯ cd  /home/username/Desktop/CUnit-2.1-3/Examples/BasicTest/
❯❯ BasicTest  20:19  pwd
/home/username/Desktop/CUnit-2.1-3/Examples/BasicTest
❯❯ BasicTest  20:19 gcc -o test_BasicTest BasicTest.c ../ExampleTests.c /opt/Cunit/lib/libcunit.a -I../ -I/opt/Cunit/include/CUnit
❯❯ BasicTest  20:21 ./test_BasicTest

说明:

  1. /opt/Cunit/lib/libcunit.a , 链接静态库
  2. -I../ , 引入 "ExampleTests.h" 头文件路径
  3. -I/opt/Cunit/include/CUnit , 引入 "Basic.h" 头文件路径

3.3 测试结果分析

❯❯ BasicTest  20:21 ./test_BasicTest

输出结果(每组前使用 “// ” 注释,实际输出不会存在)

CUnit - A unit testing framework for C - Version 2.1-3
     http://cunit.sourceforge.net/
// 
Suite: suite_success_both
  Test: testSuccess1 ...passed
  Test: testSuccess2 ...passed
  Test: testSuccess3 ...passed
Suite: suite_success_init
  Test: testSuccess1 ...passed
  Test: testSuccess2 ...passed
  Test: testSuccess3 ...passed
Suite: suite_success_clean
  Test: testSuccess1 ...passed
  Test: testSuccess2 ...passed
  Test: testSuccess3 ...passed
Suite: test_failure
  Test: testFailure1 ...FAILED
    1. ../ExampleTests.c:53  - 0
  Test: testFailure2 ...FAILED
    1. ../ExampleTests.c:54  - 0
  Test: testFailure3 ...FAILED
    1. ../ExampleTests.c:55  - 0
WARNING - Suite initialization failed for 'suite_failure_both'.
WARNING - Suite initialization failed for 'suite_failure_init'.
Suite: suite_success_but_failure_clean
  Test: testSuiteFailure1 ...FAILED
    1. ../ExampleTests.c:50  - 0
  Test: testSuiteFailure2 ...passed
WARNING - Suite cleanup failed for 'suite_success_but_failure_clean'.
Suite: TestSimpleAssert
  Test: testSimpleAssert ...FAILED
    1. ../ExampleTests.c:64  - 0
    2. ../ExampleTests.c:65  - !1
    3. ../ExampleTests.c:66  - 0
  Test: testFail ...FAILED
    1. ../ExampleTests.c:71  - CU_FAIL("This is a failure.")
    2. ../ExampleTests.c:72  - CU_FAIL("This is another failure.")
Suite: TestBooleanAssert
  Test: testAssertTrue ...FAILED
    1. ../ExampleTests.c:80  - CU_ASSERT_TRUE(!CU_TRUE)
    2. ../ExampleTests.c:81  - CU_ASSERT_TRUE(CU_FALSE)
  Test: testAssertFalse ...FAILED
    1. ../ExampleTests.c:89  - CU_ASSERT_FALSE(!CU_FALSE)
    2. ../ExampleTests.c:90  - CU_ASSERT_FALSE(CU_TRUE)
Suite: TestEqualityAssert
  Test: testAssertEqual ...FAILED
    1. ../ExampleTests.c:100  - CU_ASSERT_EQUAL(10,11)
    2. ../ExampleTests.c:101  - CU_ASSERT_EQUAL(0,1)
    3. ../ExampleTests.c:102  - CU_ASSERT_EQUAL(0,-1)
    4. ../ExampleTests.c:103  - CU_ASSERT_EQUAL(-12,12)
  Test: testAssertNotEqual ...FAILED
    1. ../ExampleTests.c:112  - CU_ASSERT_NOT_EQUAL(10,10)
    2. ../ExampleTests.c:113  - CU_ASSERT_NOT_EQUAL(0,-0)
    3. ../ExampleTests.c:114  - CU_ASSERT_NOT_EQUAL(0,0)
    4. ../ExampleTests.c:115  - CU_ASSERT_NOT_EQUAL(-12,-12)
Suite: TestPointerAssert
  Test: testAssertPtrEqual ...FAILED
    1. ../ExampleTests.c:122  - CU_ASSERT_PTR_EQUAL((void*)0x100,(void*)0x101)
  Test: testAssertPtrNotEqual ...FAILED
    1. ../ExampleTests.c:129  - CU_ASSERT_PTR_NOT_EQUAL((void*)0x100,(void*)0x100)
Suite: TestNullnessAssert
  Test: testAssertPtrNull ...FAILED
    1. ../ExampleTests.c:137  - CU_ASSERT_PTR_NULL((void*)0x23)
  Test: testAssertPtrNotNull ...FAILED
    1. ../ExampleTests.c:144  - CU_ASSERT_PTR_NOT_NULL(NULL)
    2. ../ExampleTests.c:145  - CU_ASSERT_PTR_NOT_NULL((void*)0x0)
Suite: TestStringAssert
  Test: testAssertStringEqual ...FAILED
    1. ../ExampleTests.c:156  - CU_ASSERT_STRING_EQUAL(str1,str3)
    2. ../ExampleTests.c:157  - CU_ASSERT_STRING_EQUAL(str3,str2)
  Test: testAssertStringNotEqual ...FAILED
    1. ../ExampleTests.c:169  - CU_ASSERT_STRING_NOT_EQUAL(str1,str2)
Suite: TestNStringAssert
  Test: testAssertNStringEqual ...FAILED
    1. ../ExampleTests.c:182  - CU_ASSERT_NSTRING_EQUAL(str2,str3,4)
    2. ../ExampleTests.c:183  - CU_ASSERT_NSTRING_EQUAL(str1,str3,strlen(str1))
  Test: testAssertNStringNotEqual ...FAILED
    1. ../ExampleTests.c:195  - CU_ASSERT_NSTRING_NOT_EQUAL(str1,str2,2)
    2. ../ExampleTests.c:196  - CU_ASSERT_NSTRING_NOT_EQUAL(str2,str3,2)
Suite: TestDoubleAssert
  Test: testAssertDoubleEqual ...FAILED
    1. ../ExampleTests.c:206  - CU_ASSERT_DOUBLE_EQUAL(10,10.0001,0.00001)
    2. ../ExampleTests.c:207  - CU_ASSERT_DOUBLE_EQUAL(10,10.0001,-0.00001)
    3. ../ExampleTests.c:208  - CU_ASSERT_DOUBLE_EQUAL(-10,-10.0001,0.00001)
    4. ../ExampleTests.c:209  - CU_ASSERT_DOUBLE_EQUAL(-10,-10.0001,-0.00001)
  Test: testAssertDoubleNotEqual ...FAILED
    1. ../ExampleTests.c:219  - CU_ASSERT_DOUBLE_NOT_EQUAL(10,10.001,0.01)
    2. ../ExampleTests.c:220  - CU_ASSERT_DOUBLE_NOT_EQUAL(10,10.001,-0.01)
    3. ../ExampleTests.c:221  - CU_ASSERT_DOUBLE_NOT_EQUAL(-10,-10.001,0.01)
    4. ../ExampleTests.c:222  - CU_ASSERT_DOUBLE_NOT_EQUAL(-10,-10.001,-0.01)
Suite: TestFatal
  Test: testFatal ...FAILED
    1. ../ExampleTests.c:228  - CU_FALSE

Run Summary:    Type  Total    Ran Passed Failed Inactive
              suites     16     14    n/a      3        0
               tests     35     31     10     21        0
             asserts     89     89     47     42      n/a

Elapsed time =    0.001 seconds

3.4 引用

  1. CUnit Home (sourceforge.net)
  2. CUnit - Table of Contents (sourceforge.net) Cunit User Guide

4. Unity 单元测试框架简介

Unity 也是专为 C 语言,特别是嵌入式场景开发的单元测试框架,本身由一个C文件和一系列头文件组成,移植方便。比起MinUnit,Unity 断言更为丰富,额外支持数组,位,内存等断言功能。更多内容可以直接访问下面仓库地址。

https://github.com/ThrowTheSwitch/Unity

Welcome to the Unity Test Project, one of the main projects of ThrowTheSwitch.org. Unity Test is a unit testing framework built for C, with a focus on working with embedded toolchains.

This project is made to test code targetting microcontrollers big and small. The core project is a single C file and a pair of headers, allowing it to be added to your existing build setup without too much headache. You may use any compiler you wish, and may use most existing build systems including Make, CMake, etc. If you'd like to leave the hard work to us, you might be interested in Ceedling, a build tool also by ThrowTheSwitch.org.

posted @ 2023-06-28 20:23  Oddpage  阅读(4731)  评论(0编辑  收藏  举报