Cmocka使用C进行单元测试:模拟嵌套函数调用

Cmocka使用C进行单元测试:模拟嵌套函数调用,c,unit-testing,cmocka,C,Unit Testing,Cmocka,所以,这个玩具程序复制了我在使用cmocka为现有代码开发单元测试时遇到的问题。问题在于嵌套函数调用没有模拟,这使得单元测试依赖于嵌套函数调用的正确执行。请注意,使用“mockable_static”define是因为原始代码具有作为“内部函数调用”存在的静态函数,但出于单元测试的目的,这些函数对外部调用是开放的。() 不用多说,下面是代码: 职能h: #ifndef FUNC_H_ #define FUNC_H_ #ifdef UNIT_TESTING #define mockable_st

所以,这个玩具程序复制了我在使用cmocka为现有代码开发单元测试时遇到的问题。问题在于嵌套函数调用没有模拟,这使得单元测试依赖于嵌套函数调用的正确执行。请注意,使用“mockable_static”define是因为原始代码具有作为“内部函数调用”存在的静态函数,但出于单元测试的目的,这些函数对外部调用是开放的。()

不用多说,下面是代码:

职能h:

#ifndef FUNC_H_
#define FUNC_H_

#ifdef UNIT_TESTING
#define mockable_static
mockable_static char* bar();
#endif

char* foo();

#endif // FUNC_H_
职能c:

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

#ifndef UNIT_TESTING
#define mockable_static static
#else
#define mockable_static
#endif

mockable_static char* bar (){
    printf("This is bar!\n");
    char *str = "This is the result of bar!";
    return str;
}

char* foo(){
    printf("This is foo, and it should return the results of bar()\n");
    char * res;
    res = bar();
    return res;
}
测试执行结果:

[==========] Running 2 test(s).
[ RUN      ] test_bar
This is bar!
[       OK ] test_bar
[ RUN      ] test_wrap_bar
This is a wrap and doesn't return bar!
[       OK ] test_wrap_bar
[==========] 2 test(s) run.
[  PASSED  ] 2 test(s).
[==========] Running 1 test(s).
[ RUN      ] test_foo
This is foo, and it should return the results of bar()
This is bar!
[  ERROR   ] --- "This is the result of bar!" != "I don't want bar!"
[   LINE   ] --- ./test.c:59: error: Failure!
[  FAILED  ] test_foo
[==========] 1 test(s) run.
[  PASSED  ] 0 test(s).
[  FAILED  ] 1 test(s), listed below:
[  FAILED  ] test_foo

 1 FAILED TEST(S)
Status = 1
正如您所看到的,bar()不会在foo()中进行包装,但在包装测试中,bar的包装方式与foo()调用bar的方式完全相同。Bar是使用cmocka测试库中的_real_Bar()进行测试的(而_real_Bar()是有一个原型,该函数从未定义过,并根据cmocka文档返回预期结果。有人对嵌套函数调用使用过单元测试吗?我没有发现使用cmocka模拟嵌套函数调用的任何结果,但我的google foo可能缺少。如果在测试结束时删除断言,\u foo(),由于将返回队列中未使用的值,测试失败

[==========] Running 2 test(s).
[ RUN      ] test_bar
This is bar!
[       OK ] test_bar
[ RUN      ] test_wrap_bar
This is a wrap and doesn't return bar!
[       OK ] test_wrap_bar
[==========] 2 test(s) run.
[  PASSED  ] 2 test(s).
[==========] Running 1 test(s).
[ RUN      ] test_foo
This is foo, and it should return the results of bar()
This is bar!
[  ERROR   ] --- %s() has remaining non-returned values.
./test.c:56: note: remaining item was declared here

[  FAILED  ] test_foo
[==========] 1 test(s) run.
[  PASSED  ] 0 test(s).
[  FAILED  ] 1 test(s), listed below:
[  FAILED  ] test_foo

 1 FAILED TEST(S)
Status = 1

好的,有几种不同的方法可以解决这个问题。我正在发布解决方案,以便其他人可以看到

解决方案#1:对单独的.c文件进行单独的嵌套函数调用。IE-func.c包含foo()和(newfile)bar.c包含bar()。这允许GCC--wrap=bar在func.c中工作,因为它需要链接到另一个文件

解决方案#2:为测试条和foo构建单独的测试。使用func.c中的以下行将测试条设置为“弱”

在测试foo的文件中,使用模拟条,我们重新定义bar()以充当原始的char*\uuuu wrap\u bar()函数。使用uu属性\uuuu((弱)),这个重新定义的条覆盖原始条,我们可以继续强制它在测试文件中给出我们想要的结果

生成的test_foo.c文件如下所示:

#include <setjmp.h> /* needs to be before cmocka.h */
#include <stdio.h>
#include <string.h>
#include <cmocka.h>

#include "func.h"
#include "bar.h"

static void test_bar(void **state);
static void test_wrap_bar(void **state);
static void test_foo(void **state);


char* bar(){
    printf("This is a wrap and doesn't return bar!\n");
    return (char*)mock();
}

int main(void){
    //bar test
    const struct CMUnitTest bar_tests[] = {
        cmocka_unit_test(test_wrap_bar)
    };
    const struct CMUnitTest foo_tests[] = {
        cmocka_unit_test(test_foo)
    };
    //foo test w/ mocking bar

    int status;
    status = cmocka_run_group_tests(bar_tests,NULL,NULL);
    status += cmocka_run_group_tests(foo_tests,NULL,NULL);

    printf("Status = %d\n",status);
    return status;
}

static void test_wrap_bar(void **state){
    char * this =  "I don't want bar!";
    will_return(bar,this);

    char * res = bar();
    assert_string_equal(res,this);
}

static void test_foo(void **state){
    char * this =  "I don't want bar!";
    will_return(bar,this);

    char * res = foo();
    assert_string_equal(res,this);
}
#include/*必须在cmocka.h之前*/
#包括
#包括
#包括
#包括“func.h”
#包括“bar.h”
静态孔隙试验(孔隙**状态);
静态空隙试验(空隙**状态);
静态孔隙试验(孔隙**状态);
char*bar(){
printf(“这是一个包裹,不返回条!\n”);
返回(char*)mock();
}
内部主(空){
//棒材试验
const struct CMUnitTest baru tests[]={
cmocka\u单元测试(测试包裹条)
};
const struct CMUnitTest foo_tests[]={
cmocka\u单元测试(测试foo)
};
//带模拟杆的foo测试
智力状态;
状态=cmocka\u运行\u组\u测试(条形测试,NULL,NULL);
状态+=cmocka_运行_组_测试(foo_测试,NULL,NULL);
printf(“状态=%d\n”,状态);
返回状态;
}
静态空隙测试(空隙**状态){
char*this=“我不想要酒吧!”;
将返回(条,此);
char*res=bar();
断言字符串等于(res,this);
}
静态无效测试(无效**状态){
char*this=“我不想要酒吧!”;
将返回(条,此);
char*res=foo();
断言字符串等于(res,this);
}
func.c文件为:

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

#ifndef UNIT_TEST
#define mockable_static static
#else
#define mockable_static __attribute__((weak))
#endif


mockable_static char* bar (){
    printf("This is bar!\n");
    char *str = "This is the result of bar!";
    //char *str = "This is the resfjkl;dsaj of bar!";
    return str;
}

char* foo(){
    printf("This is foo, and it should return the results of bar()\n");
    char * res;
    res = bar();
    return res;
}
#包括
#包括
#ifndef单元测试
#定义mockable_static
#否则
#定义mockable_static_uuu属性_uuu((弱))
#恩迪夫
可模拟_静态字符*条(){
printf(“这是酒吧!\n”);
char*str=“这是bar的结果!”;
//char*str=“这是酒吧的resfjkl;dsaj!”;
返回str;
}
char*foo(){
printf(“这是foo,它应该返回bar()的结果\n”);
字符*res;
res=bar();
返回res;
}
将有一个单独的文件test_bar.c,它不会重新定义bar,并且可以在func.c中测试bar()

耶,第一个解决方案是解决我自己的问题!张贴给其他人看/评论/对我大喊大叫:)

同事们,谢谢你们的帮助

__attribute__((weak))
mockable_static char* bar ().............(code follows)
#include <setjmp.h> /* needs to be before cmocka.h */
#include <stdio.h>
#include <string.h>
#include <cmocka.h>

#include "func.h"
#include "bar.h"

static void test_bar(void **state);
static void test_wrap_bar(void **state);
static void test_foo(void **state);


char* bar(){
    printf("This is a wrap and doesn't return bar!\n");
    return (char*)mock();
}

int main(void){
    //bar test
    const struct CMUnitTest bar_tests[] = {
        cmocka_unit_test(test_wrap_bar)
    };
    const struct CMUnitTest foo_tests[] = {
        cmocka_unit_test(test_foo)
    };
    //foo test w/ mocking bar

    int status;
    status = cmocka_run_group_tests(bar_tests,NULL,NULL);
    status += cmocka_run_group_tests(foo_tests,NULL,NULL);

    printf("Status = %d\n",status);
    return status;
}

static void test_wrap_bar(void **state){
    char * this =  "I don't want bar!";
    will_return(bar,this);

    char * res = bar();
    assert_string_equal(res,this);
}

static void test_foo(void **state){
    char * this =  "I don't want bar!";
    will_return(bar,this);

    char * res = foo();
    assert_string_equal(res,this);
}
#include <stdio.h>
#include <string.h>

#ifndef UNIT_TEST
#define mockable_static static
#else
#define mockable_static __attribute__((weak))
#endif


mockable_static char* bar (){
    printf("This is bar!\n");
    char *str = "This is the result of bar!";
    //char *str = "This is the resfjkl;dsaj of bar!";
    return str;
}

char* foo(){
    printf("This is foo, and it should return the results of bar()\n");
    char * res;
    res = bar();
    return res;
}