关于assert(百度百科):
编写代码时,我们总是会做出一些假设,断言就是用于在代码中捕捉这些假设,可以将断言看作是异常处理的一种高级形式。断言表示为一些布尔表达式,程序员相信在程序中的某个特定点该表达式值为真。可以在任何时候启用和禁用断言验证,因此可以在测试时启用断言,而在部署时禁用断言。同样,程序投入运行后,最终用户在遇到问题时可以重新起用断言。
assert使用情况:
1.可以在预计正常情况下程序不会到达的地方放置断言 :assert false
2.断言可以用于检查传递给私有方法的参数。(对于公有方法,因为是提供给外部的接口,所以必须在方法中有相应的参数检验才能保证代码的健壮性)
3.使用断言测试方法执行的前置条件和后置条件
4.使用断言检查类的不变状态,确保任何情况下,某个变量的状态必须满足。(如age属性应大于0小于某个合适值)
不用断言:
断言语句不是永远会执行,可以屏蔽也可以启用
因此:
1.不要使用断言作为公共方法的参数检查,公共方法的参数永远都要执行
2.断言语句不可以有任何边界效应,不要使用断言语句去修改变量和改变方法的返回值.
C里的宏:assert
功 能: 测试一个条件并可能使程序终止
用 法: void assert(int test);
#include<assert.h>#include<stdio.h>#include<stdlib.h>struct ITEM{int key;int value;};/*add item to list,make sure list is not null*/void additem(struct ITEM* itemptr){assert(itemptr!=NULL);/*additemtolist*/}int main(void){additem(NULL);return 0;}
#define assert(expr)((expr)?__ASSERT_VOID_CAST(0):__assert_fail(__STRING(expr),__FILE__,__LINE__,__ASSERT_FUNCTION))/*DefinedInGlibc2.15*/
assert的作用是先计算表达式expr,如果其值为假(即为0),那么它会打印出来assert的内容和__FILE__, __LINE__, __ASSERT_FUNCTION,然后执行abort()函数使kernel杀掉自己并coredump(是否生成coredump文件,取决于系统配置);否则,assert()无任何作用。宏assert()一般用于确认程序的正常操作,其中表达式构造无错时才为真值。完成调试后,不必从源代码中删除assert()语句,因为宏NDEBUG有定义时,宏assert()的定义为空。
在调试结束后,可以通过在包含#include <assert.h>的语句之前插入 #define NDEBUG 来禁用assert调用。
assert_module.c
#include <assert.h>#define UNIT_TESTING 1// If unit testing is enabled override assert with mock_assert().#if UNIT_TESTINGextern void mock_assert(const int result, const char* const expression,const char * const file, const int line);#undef assert#define assert(expression)mock_assert((int)(expression), #expression, __FILE__, __LINE__);#endif // UNIT_TESTINGvoid increment_value(int * const value) {assert(value);(*value) ++;}void decrement_value(int * const value) {if (value) {(*value) --;}}
其中的第2行是我添加的,这样就可以将系统的assert函数进行了一个重映射为项目实现函数。当这一行注释掉的时候,使用的为系统的assert函数,集libc里面的宏,运行的结果如下:

在使用这个宏的时候,将assert函数映射为项目里实现的断言功能,那么运行结果如下:

assert_module_test.c
#include <stdarg.h>#include <stddef.h>#include <setjmp.h>#include "cmockery.h"extern void increment_value(int * const value);/* This test case will fail but the assert is caught by run_tests() and the* next test is executed. */void increment_value_fail(void **state) {increment_value(NULL);}// This test case succeeds since increment_value() asserts on the NULL pointer.void increment_value_assert(void **state) {expect_assert_failure(increment_value(NULL));}/* This test case fails since decrement_value() doesn't assert on a NULL* pointer. */void decrement_value_fail(void **state) {expect_assert_failure(decrement_value(NULL));}int main(int argc, char *argv[]) {const UnitTest tests[] = {unit_test(increment_value_fail),unit_test(increment_value_assert),unit_test(decrement_value_fail),};return run_tests(tests);}
对于UnitTest功能之前已经有过了解,所以这里也就是相当于注册了三个测试,其中:
A: increment_value_fail函数调用increment_value(NULL);也就是相当于执行了assert(NULL)也就是执行了mock_assert(0,“value”,__FILE__, __LINE__);
mock_assert
// Replacement for assert.void mock_assert(const int result, const char* const expression,const char* const file, const int line) {if (!result) {if (global_expecting_assert) {longjmp(global_expect_assert_env, (int)expression);} else {print_error("ASSERT: %s ", expression);_fail(file, line);}}}
在这里走的是else分支,即打印了“ASSERT:value”之后,就调用_fail函数:
void _fail(const char * const file, const int line) {print_error("ERROR: " SOURCE_LOCATION_FORMAT " Failure! ", file, line);exit_test(1);}// Printf formatting for source code locations. #define SOURCE_LOCATION_FORMAT "%s:%d"
打印出了“ERROR:filename:line....”,之后调用了exit_test,在exit_test函数中调用longjmp函数进行跳转,即跳到在_run_test函数中以setjmp设置的检测点继续执行;
// Exit the currently executing test.static void exit_test(const int quit_application) {if (global_running_test) {longjmp(global_run_test_env, 1);} else if (quit_application) {exit(-1);}}
B: increment_value_assert函数调用了expect_assert_failure(increment_value(NULL));
decrement_value_fail函数调用了expect_assert_failure(decrement_value(NULL));
对于expect_assert_failure来说,这个函数其实是一个宏定义如下:
#define expect_assert_failure(function_call){const int expression = setjmp(global_expect_assert_env);global_expecting_assert = 1;if (expression) {print_message("Expected assertion %s occurred ",*((const char**)&expression));global_expecting_assert = 0;} else {function_call ;global_expecting_assert = 0;print_error("Expected assert in %s ", #function_call);_fail(__FILE__, __LINE__);}}
这里将expect_assert_failure(increment_value(NULL));函数进行展开来看,预编译之后,代码如下:
{const int expression = setjmp(global_expect_assert_env);global_expecting_assert = 1;if (expression) {print_message("Expected assertion %s occurred ",*((const char**)&expression));global_expecting_assert = 0;} else {increment_value(NULL) ;global_expecting_assert = 0;print_error("Expected assert in %s ", "increment_value(NULL)");_fail(__FILE__, __LINE__);}}
可以看到这里使用setjmp函数设置了一个全局变量global_expect_assert_env,然后进入else分支里面进行函数调用。按照正常的流程,在这里是执行不到print_error("Expected assert in %s
", "increment_value(NULL)"); 这个语句的,因为我们预期的就是错误,然后会直接在进入到increment_value函数的assert函数中
void mock_assert(const int result, const char* const expression,const char* const file, const int line) {if (!result) {if (global_expecting_assert) {longjmp(global_expect_assert_env, (int)expression);} else {print_error("ASSERT: %s ", expression);_fail(file, line);}}}
走到了if分支里,即longjmp(global_expect_assert_env, (int)expression);由这个函数将程序计数器设置到expect_assert_failure宏体展开的if分支中,即会打印出print_message("Expected assertion %s occurred
", *((const char**)&expression)); 这里需要注意,这句话的第二个参数传入的非法,正常做测试的时候会因为这个错误导致不能出现逾期的结果,如果将这句话修改为如下:print_message("Expected assertion %s occurred
", "aaaaaaaaaaaaa"); 那么程序的运行结果则会变为:

然后退出了当前这次测试。继续进行下一个测试:
同样将expect_assert_failure(decrement_value(NULL));函数进行展开来看,预编译之后,代码如下:
{const int expression = setjmp(global_expect_assert_env);global_expecting_assert = 1;if (expression) {print_message("Expected assertion %s occurred ",*((const char**)&expression));global_expecting_assert = 0;} else {decrement_value(NULL);global_expecting_assert = 0;print_error("Expected assert in %s ", "decrement_value(NULL)");_fail(__FILE__, __LINE__);}}
可以看到这里使用setjmp函数设置了一个全局变量global_expect_assert_env,然后进入else分支里面进行函数调用。按照正常的流程,在这里是执行不到print_error("Expected assert in %s
", "decrement_value(NULL)"); 这个语句的,因为我们预期的就是错误,然后会直接在decrement_value函数if判断语句为if(NULL)然后什么也不会执行,那么这个函数会正常结束,但是与我们的预期效果 expect_assert_failure 相悖,即没有出现错误,所以会出现该条测试失败,未通过的log信息。