标签 Unittest 下的文章

lcut 0.3.0版本发布

lcut单元测试框架在我的项目中应用已经有一段时间了,项目组的同事对lcut的使用也是越来越熟悉,这不今天一位同事还提出了一个新需求,需求大致是这样的。

 
在实际项目中,经常遇到这类情况:
 
int bar(…) {
    int ret;
 
    ret = foo(…);
    /* assert ret */
    …
 
    ret = foo(…);
    /* assert ret */
    …
 
    ret = foo(…);
    /* assert ret */
    …
}
 
上述代码中被测函数接口bar的实现中多次调用了某函数foo。这样当我们用mock方式测试bar这个函数时,可能需要多次重复设置foo的返回值以及输出函数的值,就像这样:
 
void tc_test_bar_return_ok(lcut_tc_t *tc, void *data) {
    LCUT_RETV_RETURN(foo, 0);
    LCUT_RETV_RETURN(foo, 0);
    LCUT_RETV_RETURN(foo, 0);
 
    LCUT_ARG_RETURN(foo, 1);
    LCUT_ARG_RETURN(foo, 1);
    LCUT_ARG_RETURN(foo, 1);
 
    LCUT_INT_EQUAL(tc, 0, bar(…));
    …
}
 
我的同事希望lcut能提供一个接口:支持一次调用,设置多次mock obj的返回值或输出参数,使用这样的接口后,上述代码就可以简化为:
 
void tc_test_bar_return_ok(lcut_tc_t *tc, void *data) {
    LCUT_RETV_RETURN_COUNT(foo, 0, 3);
    LCUT_ARG_RETURN_COUNT(foo, 1, 3);
 
    LCUT_INT_EQUAL(tc, 0, bar(…));
}
 
这个需求提的非常好,看起来更像是一种语法糖(syntactic sugar),用于简化代码编写。于是乎下午我就为lcut增加了两个有用的宏:LCUT_RETV_RETURN_COUNT和LCUT_ARG_RETURN_COUNT。
 
正如上面所说,这两个宏可在一次调用中多次设置某个mock obj的返回值和输出参数值,两个宏的原型如下:
 
#define LCUT_RETV_RETURN_COUNT(fcname, value, count) do { \
        lcut_mock_obj_return(#fcname, (void*)value, __FUNCTION__, __LINE__, __FILE__, MOCK_RETV, count); \
    } while(0);
 
#define LCUT_ARG_RETURN_COUNT(fcname, value, count) do { \
        lcut_mock_obj_return(#fcname, (void*)value, __FUNCTION__, __LINE__, __FILE__, MOCK_ARG, count); \
    } while(0);
 
只是比之前提供的LCUT_RETV_RETURN和LCUT_ARG_RETURN多了一个宏参数count。count用于指出对mocked obj进行多少次返回值或输出参数的设置。
 
另外当count传入-1时,其语义为无论mocked object被使用多少次,其返回值或输出参数值都是一样的,即使用LCUT_RETV_RETURN_COUNT或LCUT_ARG_RETURN_COUNT时设置的那个值,直到下一次调用这两个宏进行重新设置时,值才会发生变化。例如上面的例子我们也可以改写为:
 
void tc_test_bar_return_ok(lcut_tc_t *tc, void *data) {
    LCUT_RETV_RETURN_COUNT(foo, 0, -1);
    LCUT_ARG_RETURN_COUNT(foo, 1, -1);
 
    LCUT_INT_EQUAL(tc, 0, bar(…));
}
 
这样无论后续再调用多少次bar函数,foo的返回值总是0,输出参数也总是1。
 
增加了这两个宏后,lcut的版本号也小升了一位,最新版本是lcut-0.3.0-rc1,其中还增加了一个针对lcut mock功能的example – mock_test.c。同时Google Code上的lcut guide也做了更新,对新增的宏的用法进行了简要说明。
 
就这样,lcut 0.3.0版本算是发布了,后续还会经过内部的细致测试,如果没有什么问题,就会去掉rc。

lcut增加对mock的支持

记得恰好是在一个月前的今天,我发布了lcut(轻量级C语言单元测试框架)0.1.0版本
。由于发布仓促,文档没能及时跟上。在stackoverflow的一个关于单元测试的帖子
上,一位叫Craig McQueen的朋友也给出了建议:"Some documentation would be helpful. Project background and goals, a features list, advantages over existing alternatives, etc would be helpful for people who are checking it out for the first time." 看完这个建议后心里那个汗啊!不过一想到用E文编写文档心里就有些打怵。就这样在这一个月里文档依旧没有改观:(。不过,lcut本身还是有一些进步的,这两天一直规划着为lcut增加mock的支持,今天终于将这个功能加进了lcut,并发布了lcut-0.2.0-beta版,欢迎大家试用,并提出意见和建议。

之前在单元测试过程中使用cmockery中提供的mock功能,cmockery也是lcut的mock功能的直接灵感来源。与cmockery不同的是lcut将对输出参数的mock和对函数返回值的mock区分开来,这样用起来更加直观。

这里用一个简单的例子(完整代码在lcut包product_database_test.c文件中)来说明一下lcut的mock功能如何使用:

/* product_database.c */
int get_total_count_of_employee() {
    database_conn *conn = NULL;
    int retv = -1;
    int total_count = -1;

    conn = connect_to_database("tonybai", "tonybai", "mysql");
    if (!conn)
        return -1;

    retv = table_row_count(conn, "EMPLOYEE_TABLE", &total_count);
    if (retv < 0)
        return -1;
    return total_count;
}

/* product_database_test.c */
database_conn *connect_to_database(const char *user,
                                   const char *passwd,
                                   const char *serviceid) {
    return (database_conn*)LCUT_MOCK_RETV();
}

int table_row_count(const database_conn *conn,
                    const char *table_name,
                    int *total_count) {
    (*total_count) = (int)LCUT_MOCK_ARG();
    return (int)LCUT_MOCK_RETV();
}

void tc_get_total_count_of_employee_ok(lcut_tc_t *tc, void *data) {
    LCUT_RETV_RETURN(connect_to_database, 0×1234);
    LCUT_ARG_RETURN(table_row_count, 5);
    LCUT_RETV_RETURN(table_row_count, 0);

    LCUT_INT_EQUAL(tc, 5, get_total_count_of_employee());
}

被mock的函数多为系统API或执行代价较高的第三方库函数,我们在业务代码更关心的是这些函数的接口行为,而C语言中函数的接口行为表现为:返回值和输出参数。我们需要通过控制被mock函数的接口行为来达到测试我们业务代码的目的,所以我们需要mock这些函数的返回值和输出参数。上面例子中connect_to_database和table_row_count就是两个被mock了的库函数。我们通过LCUT_MOCK_RETV来mock函数的返回值,通过LCUT_MOCK_ARG来mock函数的输出参数。在测试代码tc_get_total_count_of_employee_ok中,我们分别通过LCUT_RETV_RETURN和LCUT_ARG_RETURN来控制前面两个被mock的函数中mock obj的返回值和输出参数: LCUT_RETV_RETURN(connect_to_database, 0×1234)告诉connect_to_database返回(int)0×1234,相应的,LCUT_ARG_RETURN(table_row_count, 5)则告诉table_row_count执行后其输出参数*total_count的值为5。有了这些设定的mock obj我们就可以专注于我们业务层代码的白盒逻辑单元测试了,一旦connect_to_database和table_row_count的外部行为被控制后,业务层的代码get_total_count_of_employee的行为也就是可预期的了,我们用断言测试即可。

由于实现原理限制,如果你的函数输出参数类型或返回值类型为double*/float*,那么这个函数还不能使用lcut的mock功能,否则会编译出错。但绝大多数软件开发领域都很少使用浮点计算,所以lcut的mock还是可以满足大多数需要的。

题外话:
在公司使用代理上网,svn无法直接访问google code,这个问题一直困扰着我,直到今天才知道可以为svn客户端设置代理,设置步骤如下:
-> vi ~/.subversion/servers
-> 增加如下设置:
   [global]
   http-proxy-host = 你的代理主机域名或ip
   http-proxy-port = 端口
   http-proxy-username = 你的用户名
   http-proxy-password = 你的密码
设置后,svn立马就可以连上google code的svn server了。

如发现本站页面被黑,比如:挂载广告、挖矿等恶意代码,请朋友们及时联系我。十分感谢! Go语言第一课 Go语言精进之路1 Go语言精进之路2 商务合作请联系bigwhite.cn AT aliyun.com

欢迎使用邮件订阅我的博客

输入邮箱订阅本站,只要有新文章发布,就会第一时间发送邮件通知你哦!

这里是 Tony Bai的个人Blog,欢迎访问、订阅和留言! 订阅Feed请点击上面图片

如果您觉得这里的文章对您有帮助,请扫描上方二维码进行捐赠 ,加油后的Tony Bai将会为您呈现更多精彩的文章,谢谢!

如果您希望通过微信捐赠,请用微信客户端扫描下方赞赏码:

如果您希望通过比特币或以太币捐赠,可以扫描下方二维码:

比特币:

以太币:

如果您喜欢通过微信浏览本站内容,可以扫描下方二维码,订阅本站官方微信订阅号“iamtonybai”;点击二维码,可直达本人官方微博主页^_^:
本站Powered by Digital Ocean VPS。
选择Digital Ocean VPS主机,即可获得10美元现金充值,可 免费使用两个月哟! 著名主机提供商Linode 10$优惠码:linode10,在 这里注册即可免费获 得。阿里云推荐码: 1WFZ0V立享9折!


View Tony Bai's profile on LinkedIn

文章

评论

  • 正在加载...

分类

标签

归档



View My Stats