CMocka基础使用

Author:Once Day Date:2023年6月15日

参考文档:

1. 概述
1.1 介绍

CMocka 是一个用于 C 语言的单元测试框架,它是 Check 单元测试框架的一个分支。CMocka 的设计哲学是“只做一件事,但做得好”,这使得它成为一个轻量级、易于使用的工具。它为测试 C 代码提供了一种简单、稳定的方法。

以下是使用 CMocka 的一些主要特性:

  1. 轻量级和模块化:CMocka 不需要复杂的设置,不依赖于其他库,且不会大幅度地增加你的项目大小。你可以简单地将其添加到你的 C 项目中,然后开始编写测试。

  2. 模拟函数:在进行单元测试时,我们通常需要模拟一些函数以便在特定条件下测试我们的代码。CMocka 提供了一个功能强大的模拟函数系统,你可以方便地使用它来模拟你的函数。

  3. 断言:CMocka 提供了一组断言宏,你可以使用这些断言来验证你的代码的行为。这些断言会在测试失败时提供有用的错误信息。

  4. 组织测试:CMocka 提供了一种简单的方式来组织你的测试。你可以将测试分组,然后一次运行一个组,或者运行所有组的测试。

  5. 端到端的测试:与只关注单元测试的一些其他框架不同,CMocka 也支持端到端的测试。这使得你可以在一个统一的环境中进行所有的测试。

以下是一个简单的 CMocka 测试用例示例:

#include <stdarg.h>
#include <stddef.h>
#include <setjmp.h>
#include <cmocka.h>

// The function to test
int add(int a, int b) {
    return a + b;
}

// The test case function
void test_add(void **state) {
    assert_int_equal(add(2, 2), 4);
}

int main(void) {
    const struct CMUnitTest tests[] = {
        cmocka_unit_test(test_add),
    };

    return cmocka_run_group_tests(tests, NULL, NULL);
}

在这个例子中,我们首先定义了一个名为 add 的函数,然后定义了一个测试用例 test_add,这个测试用例使用 assert_int_equal 断言来检查 add 函数的结果是否正确。最后,我们在 main 函数中运行这个测试用例。

更多关于 CMocka 的信息,你可以参考其官方文档

1.2 特性

这个项目的目标是在不同的平台和操作系统上为C提供一个强大的测试框架,它只需要标准的C库。

  • 支持模拟对象,Mock对象是模拟对象,用于模拟实际对象的实际实现。可用于模拟接口的依赖项,以帮助单独测试接口。一些项目使用mock功能来模拟通过网络通信的组件。
  • 只需要一个C库,这使得cmocka可以在许多嵌入式平台上工作。
  • 几种支持的输出格式,cmocka支持几种不同的消息输出格式,如Test Anything Protocol、Subunit、xUnit XML或原始cmock输出格式。
  • 完全文档化的API,该API有很好的文档记录,cmoka为它提供的不同特性提供了几个示例。
  • 测试fixture,测试fixture是可以在多个测试用例之间共享的设置和拆除功能,以提供准备测试环境并在之后销毁测试环境的公共功能。
  • 信号(SIGSEGV, SIGILL,…)的异常处理,如果出现异常,如段故障,cmoka能够恢复测试状态。
  • 没有fork() ,Cmocka在测试用例中不使用fork()进行异常处理。
  • 经过了很好的测试,Cmocka有每晚夜间构建,可以在多个平台和不同的编译器上进行测试,以确保它能正常工作。如果你想让它在你的平台上工作,你可以添加一个每晚夜间夜间构建,我们将确保不会破坏你的平台或编译器。
  • 测试内存泄漏,缓冲区溢出和下溢。我们在测试过程中提供了一些功能,这些功能可以在不运行valgrind等工具的情况下检测到某些类型的内存分配问题。
  • cmockery兼容支持,Cmocka是cmock的派生和继承者。我们提供了cmock头文件的安装,以便您为cmock编写的测试在迁移到cmock之前仍然可以工作。
  • 平台和编译器,cmocka在Linux、BSD、Solaris、Windows和嵌入式平台上运行良好。众所周知,它可以与GCC、LLVM、MSVC、MinGW等一起工作。

CMocka 使用 Apache License 2.0 进行许可。这是一个宽松的开源许可证,允许你自由地使用、复制和分发软件,无论是在开源还是闭源的项目中。

Apache License 2.0 的主要特点包括:

  1. 商业友好:你可以在你的商业产品中使用和分发使用 Apache License 2.0 许可的软件,而无需支付费用。

  2. 版权声明:你必须在你分发的所有副本和重要部分的版权声明中,保留使用 Apache License 2.0 许可的软件的版权声明。

  3. 状态变更:如果你修改了使用 Apache License 2.0 许可的软件,你必须在修改的文件中添加一个明显的标记,表示你做了修改。

  4. 免责声明:Apache License 2.0 包含一个免责声明,声明软件是“按原样”提供,不提供任何明示或暗示的保证。

请注意,这只是 Apache License 2.0 的一个简单概述,对于具体的法律条款和细节,应该参考完整的 Apache License 2.0 文本

1.3 注意事项

嵌入式平台,有些嵌入式平台可能不提供所需类型的定义,或者保护它们的防护措施没有定义。为了解决这个问题,你可以创建一个名为 ‘cmocka_platform.h’ 的头文件,其中包含所需的类型和定义。然后,使用以下命令将 cmake 指向包含目录:

cmake -DCMOCKA_PLATFORM_INCLUDE=/home/compiler/my/include_directory ..

线程,CMocka 并不完全线程安全,它的目标也不是要实现线程安全。我们有几个全局变量来跟踪测试状态。它们被标记为线程本地的,但你可能会遇到问题。然而,如果你在使用线程的应用程序中编写测试,你可以设置以下环境变量:

CMOCKA_TEST_ABORT='1' ./my_threading_test

将此环境变量设置为 ‘1’,如果测试失败,CMocka 将调用 abort() 函数。

CMocka 提供了多种测试结果的输出格式。特定的格式可以通过使用 CMOCKA_MESSAGE_OUTPUT 环境变量来配置。此变量接受几个值,每个值对应一个不同的输出格式

  • STDOUT:这是默认的输出格式。它将人类可读的测试输出打印到标准输出。
  • SUBUNIT:该选项将以 Subunit 格式生成输出,这是一种允许测试工具以机器可读的格式交流测试结果的协议。
  • TAP:此选项将以 Test Anything Protocol (TAP) 格式生成输出。TAP 是一种文本基础的协议,用于以标准化的方式报告测试结果,从而允许其他工具进行简单的解析。
  • XML:此选项将以 JUnit XML 格式生成输出。这是一种报告测试结果的标准 XML 格式,广泛用于持续集成服务器。

所有这些选项都不区分大小写。默认情况下,XML 输出会发送到标准错误(stderr)。但是,如果设置了 CMOCKA_XML_FILE 环境变量并指向一个不存在的文件,CMocka 将把 XML 输出写入该文件。

如果你在多个组中运行测试并希望为每个组生成一个单独的 XML 文件,可以将 CMOCKA_XML_FILE 设置为 CMOCKA_XML_FILE=cm_%g.xml。这里的 %g 占位符将被每个测试的组名替换,为每个组创建一个唯一的文件。如果你不这样设置,所有组的结果将被打印到同一个文件中。

2. 基本用法
2.1 MOCK对象使用

模拟对象(Mock objects)是模拟真实对象行为的仿真对象。在测试中,被测试对象不会调用真实对象,而是调用一个模拟对象,该模拟对象仅仅断言是否以正确的顺序、以预期的参数调用了正确的方法。

will_return(function, value) - will_return() 宏将一个值压入模拟值堆栈中。这个宏主要由单元测试本身使用,以编程模拟对象的行为。

mock() - mock 宏从测试值堆栈中弹出一个值。mock() 宏的用户是模拟对象,使用它来了解自己应该如何行为。
由于 will_return()mock() 预期要成对使用,如果使用 will_return() 将更多的值压入堆栈而未通过 mock() 消耗,或者反之,则 cmocka 库会使测试失败。

以下的单元测试示例说明了如何指示模拟对象返回特定的值:

#include <stdarg.h>
#include <stddef.h>
#include <setjmp.h>
#include <cmocka.h>

/* 假设这是我们要模拟的函数 */
int function_to_mock(int arg) {
    return (int)mock();
}

/* 我们的单元测试 */
void test_function(void **state) {
    /* 指示模拟函数返回42 */
    will_return(function_to_mock, 42);

    /* 在这里,我们调用了函数,它会调用我们的模拟函数 */
    int result = some_function_that_calls_function_to_mock();

    /* 验证结果 */
    assert_int_equal(result, 42);
}

int main(void) {
    const struct CMUnitTest tests[] = {
        cmocka_unit_test(test_function),
    };
    return cmocka_run_group_tests(tests, NULL, NULL);
}

在这个例子中,some_function_that_calls_function_to_mock() 是调用我们模拟的函数的函数。我们通过 will_return 告诉模拟函数返回42,然后验证返回的结果是否正确。

目前有以下函数可以使用:

LargestIntegralType mock (void)
 	Retrieve a return value of the current function. 
type mock_ptr_type (#type)
 	Retrieve a typed return value of the current function. 
void will_return (#function, LargestIntegralType value)
 	Store a value to be returned by mock() later. 
void will_return_count (#function, LargestIntegralType value, int count)
 	Store a value to be returned by mock() later. 
void will_return_always (#function, LargestIntegralType value)
 	Store a value that will be always returned by mock().
void will_return_maybe (#function, LargestIntegralType value)
 	Store a value that may be always returned by mock().
2.2 检查MOCK函数输入参数

当我们需要对传入模拟函数的参数进行检查时,可以使用 expect_*() 函数(如 expect_string(), expect_value() 等)来设定预期的参数值。然后,在模拟函数中,我们可以使用 check_expected() 宏来检查实际参数是否符合预期。

如果连续调用同一个参数的 expect_*() 宏,那么这些预期值将会被依次加入队列。check_expected() 会将函数参数与使用 expect_*() 队列中的下一个值进行比较。如果参数检查失败,测试将标记为失败。此外,如果调用 check_expected() 时,队列中已无参数值,测试也将标记为失败。

以下是如何使用这些功能的示例。首先,我们在测试驱动中调用一个函数:

static void test_driver(void **state)
{
    expect_string(chef_cook, order, "hotdog");
}

现在,chef_cook 函数可以检查传入的参数是否符合测试驱动的预期。这可以通过以下方式完成:

int chef_cook(const char *order, char **dish_out)
{
    check_expected(order);
}

在这个例子中,test_driver 函数预期 chef_cookorder 参数为 “hotdog”。然后,在 chef_cook 函数中,我们通过 check_expected(order) 来检查实际的 order 参数是否为 “hotdog”。

目前有以下函数可以使用:

void expect_check (#function, #parameter, #check_function, const void *check_data)
 	Add a custom parameter checking function. 
void expect_in_set (#function, #parameter, LargestIntegralType value_array[])
 	Add an event to check if the parameter value is part of the provided array. 
void expect_in_set_count (#function, #parameter, LargestIntegralType value_array[], size_t count)
 	Add an event to check if the parameter value is part of the provided array.
void expect_not_in_set (#function, #parameter, LargestIntegralType value_array[])
 	Add an event to check if the parameter value is not part of the provided array.
void expect_not_in_set_count (#function, #parameter, LargestIntegralType value_array[], size_t count)
 	Add an event to check if the parameter value is not part of the provided array.
void expect_in_range (#function, #parameter, LargestIntegralType minimum, LargestIntegralType maximum)
 	Add an event to check a parameter is inside a numerical range. The check would succeed if minimum <= value <= maximum.
void expect_in_range_count (#function, #parameter, LargestIntegralType minimum, LargestIntegralType maximum, size_t count)
 	Add an event to repeatedly check a parameter is inside a numerical range. The check would succeed if minimum <= value <= maximum.
void expect_not_in_range (#function, #parameter, LargestIntegralType minimum, LargestIntegralType maximum)
 	Add an event to check a parameter is outside a numerical range. The check would succeed if minimum > value > maximum.
void expect_not_in_range_count (#function, #parameter, LargestIntegralType minimum, LargestIntegralType maximum, size_t count)
 	Add an event to repeatedly check a parameter is outside a numerical range. The check would succeed if minimum > value > maximum.
void expect_value (#function, #parameter, LargestIntegralType value)
 	Add an event to check if a parameter is the given integer based value.
void expect_value_count (#function, #parameter, LargestIntegralType value, size_t count)
 	Add an event to repeatedly check if a parameter is the given integer based value.
void expect_not_value (#function, #parameter, LargestIntegralType value)
 	Add an event to check if a parameter isn't the given value.
void expect_not_value_count (#function, #parameter, LargestIntegralType value, size_t count)
 	Add an event to repeatedly check if a parameter isn't the given value.
void expect_string (#function, #parameter, const char *string)
 	Add an event to check if the parameter value is equal to the provided string.
void expect_string_count (#function, #parameter, const char *string, size_t count)
 	Add an event to check if the parameter value is equal to the provided string.
void expect_not_string (#function, #parameter, const char *string)
 	Add an event to check if the parameter value isn't equal to the provided string.
void expect_not_string_count (#function, #parameter, const char *string, size_t count)
 	Add an event to check if the parameter value isn't equal to the provided string.
void expect_memory (#function, #parameter, void *memory, size_t size)
 	Add an event to check if the parameter does match an area of memory.
void expect_memory_count (#function, #parameter, void *memory, size_t size, size_t count)
 	Add an event to repeatedly check if the parameter does match an area of memory.
void expect_not_memory (#function, #parameter, void *memory, size_t size)
 	Add an event to check if the parameter doesn't match an area of memory.
void expect_not_memory_count (#function, #parameter, void *memory, size_t size, size_t count)
 	Add an event to repeatedly check if the parameter doesn't match an area of memory.
void expect_any (#function, #parameter)
 	Add an event to check if a parameter (of any value) has been passed.
void expect_any_always (#function, #parameter)
 	Add an event to always check if a parameter (of any value) has been passed.
void expect_any_count (#function, #parameter, size_t count)
 	Add an event to repeatedly check if a parameter (of any value) has been passed.
void check_expected (#parameter)
 	Determine whether a function parameter is correct.
void check_expected_ptr (#parameter)
 	Determine whether a function parameter is correct.
2.3 断言宏

CMocka 提供的一系列有用的断言宏(assert macros),类似于标准 C 库的 assert(3) 宏。

在断言失败时,CMocka 的断言宏会将失败写入标准错误流,并标记测试失败。由于 C 语言的限制,通用的 C 标准库 assert() 以及 CMocka 的 assert_true()assert_false() 宏只能显示导致断言失败的表达式。而 CMocka 的类型特定断言宏,例如 assert_{type}_equal()assert_{type}_not_equal(),会显示导致断言失败的数据,这增加了数据可见性,有助于调试失败的测试用例。

以下是一些 CMocka 断言宏的例子:

  • assert_true(expression):断言表达式的结果为真(非零)。如果表达式结果为假(零),则测试失败。

  • assert_false(expression):断言表达式的结果为假(零)。如果表达式结果为真(非零),则测试失败。

  • assert_int_equal(a, b):断言两个整数相等。如果不相等,测试失败,并显示两个整数的值。

  • assert_string_equal(a, b):断言两个字符串相等。如果不相等,测试失败,并显示两个字符串的值。

这些断言宏极大地方便了单元测试的编写和结果的判断。

目前有以下函数可以使用:

void assert_true (scalar expression)
 	Assert that the given expression is true.
void assert_false (scalar expression)
 	Assert that the given expression is false.
void assert_return_code (int rc, int error)
 	Assert that the return_code is greater than or equal to 0.
void assert_non_null (void *pointer)
 	Assert that the given pointer is non-NULL.
void assert_null (void *pointer)
 	Assert that the given pointer is NULL.
void assert_ptr_equal (void *a, void *b)
 	Assert that the two given pointers are equal.
void assert_ptr_not_equal (void *a, void *b)
 	Assert that the two given pointers are not equal.
void assert_int_equal (int a, int b)
 	Assert that the two given integers are equal.
void assert_int_not_equal (int a, int b)
 	Assert that the two given integers are not equal.
void assert_float_equal (float a, float b, float epsilon)
 	Assert that the two given float are equal given an epsilon.
void assert_float_not_equal (float a, float b, float epsilon)
 	Assert that the two given float are not equal given an epsilon.
void assert_double_equal (double a, double b, double epsilon)
 	Assert that the two given double are equal given an epsilon.
void assert_double_not_equal (double a, double b, double epsilon)
 	Assert that the two given double are not equal given an epsilon.
void assert_string_equal (const char *a, const char *b)
 	Assert that the two given strings are equal.
void assert_string_not_equal (const char *a, const char *b)
 	Assert that the two given strings are not equal.
void assert_memory_equal (const void *a, const void *b, size_t size)
 	Assert that the two given areas of memory are equal, otherwise fail.
void assert_memory_not_equal (const void *a, const void *b, size_t size)
 	Assert that the two given areas of memory are not equal.
void assert_in_range (LargestIntegralType value, LargestIntegralType minimum, LargestIntegralType maximum)
 	Assert that the specified value is not smaller than the minimum and and not greater than the maximum.
void assert_not_in_range (LargestIntegralType value, LargestIntegralType minimum, LargestIntegralType maximum)
 	Assert that the specified value is smaller than the minimum or greater than the maximum.
void assert_in_set (LargestIntegralType value, LargestIntegralType values[], size_t count)
 	Assert that the specified value is within a set.
void assert_not_in_set (LargestIntegralType value, LargestIntegralType values[], size_t count)
 	Assert that the specified value is not within a set.
2.4 函数调用顺序

这段文字说明了如何使用 CMocka 的 expect_function_call()function_called() 来验证函数调用的顺序。这与模拟返回值和参数检查是独立的,因为这两者并不检查它们从不同函数中被调用的顺序。

expect_function_call(function) - expect_function_call() 宏将预期调用推入预期调用堆栈。

function_called() - 从预期调用堆栈中弹出一个值。function_called() 在使用它的模拟对象中被调用。

expect_function_call()function_called() 预期成对使用。如果创建的预期调用(例如 expect_function_call())比通过 function_called() 消耗的预期调用多或少,CMocka 将使测试失败。在测试中,如果代码中的模拟调用并非测试的关注点,可以使用 ignore_function_calls() 等方法来绕过这个限制。function_called() 必须在与 expect_function_call() 相同的线程中调用,并且该线程必须已经被 CMocka 初始化(详见主文档页面的线程部分)。

以下的例子说明了如何在单元测试中指示 CMocka 预期从特定模拟 chef_sing() 中调用 function_called()

void chef_sing(void);
 
void code_under_test()
{
  chef_sing();
}
 
void some_test(void **state)
{
    expect_function_call(chef_sing);
    code_under_test();
}

然后,模拟的实现必须通过调用 function_called() 来检查是否应该被调用:

void chef_sing()
{
    function_called();
}

在这个例子中,some_test 函数预期 chef_sing() 会被调用。然后,在 chef_sing 函数中,我们通过 function_called() 来验证这个预期。

目前可用的函数如下:

void function_called (void)
 	Check that current mocked function is being called in the expected order.
void expect_function_calls (#function, const int times)
 	Store expected call(s) to a mock to be checked by function_called() later.
void expect_function_call (#function)
 	Store expected single call to a mock to be checked by function_called() later.
void expect_function_call_any (#function)
 	Expects function_called() from given mock at least once.
void ignore_function_calls (#function)
 	Ignores function_called() invocations from given mock function
2.5 运行测试

下面的例子说明了如何使用 cmocka_unit_test 宏和 cmocka_run_group_tests 函数运行测试:

void Test0(void **state);
void Test1(void **state);
 
int main(void)
{
    const struct CMUnitTest tests[] = {
        cmocka_unit_test(Test0),
        cmocka_unit_test(Test1),
    };
 
    return cmocka_run_group_tests(tests, NULL, NULL);
}

在这个例子中,我们有两个测试函数 Test0Test1。这些函数的参数是一个 void ** 指针,这是 CMocka 的标准测试函数签名。这个参数可以用于传递测试特定的状态或数据。

我们使用 cmocka_unit_test 宏将这些测试函数包装成 CMUnitTest 结构体,并放入一个数组中。

然后,我们使用 cmocka_run_group_tests 函数来运行这些测试。这个函数接受一个 CMUnitTest 结构体的数组,以及两个可选的回调函数,这些回调函数在测试组的开始和结束时被调用。在这个例子中,我们没有提供这些回调函数,所以传入 NULL

cmocka_run_group_tests 函数返回一个整数,表示测试的结果。如果所有测试都通过了,返回值为 0。如果有任何测试失败,返回值为非 0。这个返回值可以被用作程序的退出代码,以便于在脚本或连续集成系统中检查测试结果。

目前可用的宏定义:

#define 	unit_test(f)  
	{ #f, f, UNIT_TEST_FUNCTION_TYPE_TEST }
#define 	_unit_test_setup(test, setup)
	{ #test "_" #setup, setup, UNIT_TEST_FUNCTION_TYPE_SETUP }
#define 	unit_test_setup(test, setup)
#define 	_unit_test_teardown(test, teardown) 
	{ #test "_" #teardown, teardown, UNIT_TEST_FUNCTION_TYPE_TEARDOWN }
#define 	unit_test_teardown(test, teardown)
#define 	group_test_setup(setup)    
	{ "group_" #setup, setup, UNIT_TEST_FUNCTION_TYPE_GROUP_SETUP }
#define 	group_test_teardown(teardown)    
	{ "group_" #teardown, teardown, UNIT_TEST_FUNCTION_TYPE_GROUP_TEARDOWN }
#define 	unit_test_setup_teardown(test, setup, teardown)
#define 	cmocka_unit_test(f)   
	{ #f, f, NULL, NULL, NULL }
#define 	cmocka_unit_test_setup(f, setup)
	{ #f, f, setup, NULL, NULL }
#define 	cmocka_unit_test_teardown(f, teardown)
	{ #f, f, NULL, teardown, NULL }
#define 	cmocka_unit_test_setup_teardown(f, setup, teardown)
	{ #f, f, setup, teardown, NULL }
#define 	cmocka_unit_test_prestate(f, state)
	{ #f, f, NULL, NULL, state }
#define 	cmocka_unit_test_prestate_setup_teardown(f, setup, teardown, state)
	{ #f, f, setup, teardown, state }
#define 	run_tests(tests)
	_run_tests(tests, sizeof(tests) / sizeof((tests)[0]))
#define 	run_group_tests(tests)
	_run_group_tests(tests, sizeof(tests) / sizeof((tests)[0]))

目前可用的函数定义:

void 	fail (void)
 	Forces the test to fail immediately and quit.
void 	skip (void)
 	Forces the test to not be executed, but marked as skipped.
void 	fail_msg (const char *msg,...)
 	Forces the test to fail immediately and quit, printing the reason.
int 	run_test (#function)
 	Generic method to run a single test.
int 	cmocka_run_group_tests (const struct CMUnitTest group_tests[], CMFixtureFunction group_setup, CMFixtureFunction group_teardown)
 	Run tests specified by an array of CMUnitTest structures.
int 	cmocka_run_group_tests_name (const char *group_name, const struct CMUnitTest group_tests[], CMFixtureFunction group_setup, CMFixtureFunction group_teardown)
 	Run tests specified by an array of CMUnitTest structures and specify a name. More...
2.6 内存检查

如果要测试内存泄漏、缓冲区溢出和下溢,被 CMocka 测试的模块应将对 malloc(), calloc()free() 的调用替换为 test_malloc(), test_calloc()test_free()。每次使用 test_free() 释放一个块时,它都会被检查是否有损坏,如果找到损坏的块,则标记测试失败。CMocka 库跟踪所有使用 test_*() 分配函数分配的块。当测试完成时,如果有任何分配的块(内存泄漏)仍然存在,它们将被报告,并标记测试失败。

为了简单起见,CMocka 目前在一个进程中执行所有测试。因此,测试应用程序中的所有测试用例共享一个单一的地址空间,这意味着单个测试用例的内存损坏可能会导致测试应用程序提前退出。

下面是一些例子:

void some_function_under_test()
{
    char *data = test_malloc(100);
    // Do something with data
    test_free(data);
}

void some_test(void **state)
{
    some_function_under_test();
    // If some_function_under_test() didn't call test_free(), a memory leak would be reported
}

在这个例子中,我们在被测试的函数中使用 test_malloc() 分配了一些内存,然后在结束时使用 test_free() 释放了它。如果我们没有调用 test_free(),CMocka 就会报告一个内存泄漏。

目前可用的函数定义:

void * 	test_malloc (size_t size)
 	Test function overriding malloc.
void * 	test_calloc (size_t nmemb, size_t size)
 	Test function overriding calloc.
void * 	test_realloc (void *ptr, size_t size)
 	Test function overriding realloc which detects buffer overruns and memoery leaks.
void 	test_free (void *ptr)
 	Test function overriding free(3).
2.7 标准断言

像标准 C 库的 assert() 这样的运行时断言宏应该在被测试的模块中被重定义,以使用 CMocka 的 mock_assert() 函数。通常,mock_assert() 将标记一个测试失败。如果一个函数是用 expect_assert_failure() 宏调用的,那么函数内的任何 mock_assert() 调用都将导致测试的执行。如果在通过 expect_assert_failure() 调用的函数期间没有调用 mock_assert(),则会发出测试失败的信号。例如,你可以这样重定义 assert()

#undef assert
#define assert(value) mock_assert((int)(value), #value, __FILE__, __LINE__)

然后,你可以使用 expect_assert_failure() 来测试预期的断言失败:

void function_under_test()
{
    assert(0); // This will call mock_assert()
}

void some_test(void **state)
{
    expect_assert_failure(function_under_test);
    // If function_under_test() didn't call mock_assert(), a test failure would be reported
}

在这个例子中,我们预期 function_under_test() 会调用 assert(0),这实际上会调用 mock_assert()。如果 function_under_test() 没有调用 mock_assert(),CMocka 就会报告测试失败。

目前可用的函数定义如下:

void 	mock_assert (const int result, const char *const expression, const char *const file, const int line)
 	Function to replace assert(3) in tested code.
void 	expect_assert_failure (function fn_call)
 	Ensure that mock_assert() is called.
11-06 02:17