标签 Programmer 下的文章

C单元测试之使用cmockery

这么久以来一直没有找到一款很好的支持mock测试的C语言单元测试工具包,但前不久在一网友的评论中得知:去年Google曾发布了一款c语言的轻量级单元测试framework — “cmockery”,cmcokery很小巧,对其他开源包没有依赖,对被测试代码侵入性小;它支持mock test,同样也可以支持常规的单元测试。

之前在博客中曾描述过C语言实现mock的一个思路,不过和cmockery对比起来,当时我的思路显然还处于初级阶段,而cmockery则走到了更高级,使用起来也更为简便。

还是以我上一篇文章中的代码来举例,利用cmockery来对biz.c进行mock test。

应用层被测试代码不变:
/* biz.h */
#ifndef BIZ_H
#define BIZ_H

#include

int biz_operation(char *fname);
#endif

/* biz.c */
#include "biz.h"

int biz_operation(char *fname) {
        FILE *fp = NULL;

        fp = fopen(fname, "r");
        if (fp == NULL) {
                printf("fail to open fle!\n");
                return 1;
        } else {
                printf("succeed to open file!\n");
                return 0;
        }
}

利用cmockery改造测试代码如下:
/* test.c */
#include
#include
#include
#include
#include "cmockery.h"
#include "biz.h"

FILE *fopen(const char *filename, const char *mode) {
        return (FILE*)mock();
}

void test_biz_operation_return_succ(void **state) {
        will_return(fopen, 0×1234);
        assert_true(biz_operation("foo.txt") == 0);
}

void test_biz_operation_return_fail(void **state) {
        will_return(fopen, NULL);
        assert_true(biz_operation("foo.txt") == 1);
}

int main() {
        const UnitTest tests[] = {
                unit_test(test_biz_operation_return_succ),
                unit_test(test_biz_operation_return_fail),
    };
        return run_tests(tests);
}

gcc  biz.c test.c -I ./ -I {YOUR_CMOCKERY_INSTALL_DIR}/include/google -L {YOUR_CMOCKERY_INSTALL_DIR}/lib -lcmockery

执行a.out结果如下:
test_biz_operation_return_succ: Starting test
succeed to open file!
test_biz_operation_return_succ: Test completed successfully.
test_biz_operation_return_fail: Starting test
fail to open fle!
test_biz_operation_return_fail: Test completed successfully.
All 2 tests passed

在测试代码中override了C标准库中fopen的实现,代码很简单,就是调用一个mock,然后根据返回值类型做一个强制类型转换。那么执行
起来后mock究竟会返回什么值呢?这个值你可以任意设定,看到下面test_biz_operation_return_xx中的
will_return宏了吗,在will_return中你可以设定fopen中mock()调用的返回值:will_return(fopen,
0×1234)或will_return(fopen, NULL);
是不是思路清晰了许多了呢,没错。cmockery就是通过在will_return中设置mock的返回值,并在执行fopen这类被mock的函数接
口中通过接口实现中的mock调用将你设定的值返回出来,从而控制被测试接口biz_operation的执行路径和执行结果,以达到mock
test的目的。

cmockery的源代码行数不到3K,你阅读一下will_return和mock的源代码就一目了然了。大致思路应
该是:在堆上分配一块内存,用来存储你在will_return中设定的返回值,用函数名字符串做索引;在mock()中则通过调用mock的函数的名字
去匹配,得到已经设定好的存储在堆内存上的那个值,并在转型后返回。

以上是cmockery支持的对通用函数返回值的
mock,cmockery还支持mock function的parameters checking、setup和teardown、assert
failure和dynamic memory allocation的mock等,cmockery的manual
有细致说明。不过原理都类似,在上面也都说过了。不过有些机制对被测试代码还是有一定侵入性的。cmockery虽好,但是毕竟是针对C语言的,就无法逃
过链接阶段符号resolved的问题,测试上面简单的代码还好,如果测试大工程中某复杂源代码文件中的接口时,链接时就会有些麻烦,所以工程源代码组织
的扁平化、接口功能单一化、包含和清晰的调用关系都会大大有助于实施mock单元测试,否则一旦你的biz_operation接口功能很复杂,调用了很
多其他接口,实现很冗长的话,那么你的mock测试同样也会很麻烦,这时你需要做的就应该是重构你的代码,将复杂的biz_operation拆分开了。

另外cmockery的安装很简单,遵循常规的configure->make->make install,这里不再贽述了。

分享一个Oracle OCI库的BUG

上周测试组反馈在一台HP X86-64主机Solaris 10 for X86环境下部署的应用无法连接Oracle数据库,错误码ORA-12154。而另外一个产品的部署在这台主机上的应用却能正常连接到数据库。本周安排专人对该问题进行查找,在先后排除了用户环境设置、Oracle数据库服务端等问题后,我们最终把目光集中在了Oracle客户端的OCI库上。

定位过程如下:
1、SQLPLUS可以访问数据库;
2、同环境下另一个应用可以访问数据库;
以上证明用户环境和tnsnames.ora配置没有问题;
3、通过抓包未发现客户端有到Oracle服务端的链接和数据传输,所以该问题应该与Oracle Server端一毛钱关系都没有;
4、发现我们产品的应用使用的是32bit库编译的,而另外一个产品的应用使用的是64bit库,但两个产品底层调用都是一样的;
5、基本锁定是该主机上装的Oracle OCI 32bit库有bug;
6、我们的资深系统工程师在Oracle官方找到了该问题的根源;
7、安装新patch后,应用顺利连接到Oracle Server,问题解决。

Oracle官方对该问题的说明摘录如下:
Solaris x86-64: Running 32-bit Applications Connecting to Database Using TNS Naming Adapter Fails With Segmentation Fault (SIGSEGV) or ORA-12154
  Doc ID:  388631.1 Type:  PROBLEM
  Modified Date :  23-OCT-2007 Status:  PUBLISHED

——————————————————————————–
Applies to:
Oracle Server – Enterprise Edition – Version: 10.2.0.2

Symptoms
Running 32-bit applications connecting to Database using TNS Naming Adapter Fails With Segmentation Violation (SIGSEGV)

Segmentation Fault(coredump)

Running 64-bit work as expected.

Other symptoms would be

ORA-12154: TNS:could not resolve the connect identifier specified

Cause
This has been identified to be caused by
Bug 5389730 10.2.0.1 32BIT OCI EXECUTABLES FAILS WITH ORA-12154 ON SOLARIS 10 X86-64(AMD64)

TNS Naming Adapter was not included within the 32-bit Naming Libraries.

Solution
This is fixed Oracle11g Client 11.0.

There exists patches for 10.2.0.2 and 10.2.0.3:

download and installPatch 5389730 with opatch or

To implement the solution manually, please execute the following steps:

Download Patch 5389730
cp $ORACLE_HOME/network/lib/ins_net_client.mk
$ORACLE_HOME/network/lib/ins_net_client.mk.prePatch_5389730
extract ins_net_client.mk into $ORACLE_HOME/network/lib/ins_net_client.mk
cd $ORACLE_HOME/network/lib
make -f ins_net_client.mk nnfgt.o
Which update (check this)
$ORACLE_HOME/lib/libn10.a and $ORACLE_HOME/lib32/libn10.a
make -f ins_net_client.mk client_sharedlib

which update (check this)
#$ORACLE_HOME/lib32/libclntsh.so
#$ORACLE_HOME/lib32/libclntsh.so.10.1
#$ORACLE_HOME/lib/libclntsh.so
#$ORACLE_HOME/lib/libclntsh.so.10.1

Check that executable is loading $ORACLE_HOME/lib32/libclntsh.so.10.1 by ldd ‘executable’

All dynamically linked applications that use libclntsh should work now.
Static linked applications, need to be relinked with the new libraries.

如发现本站页面被黑,比如:挂载广告、挖矿等恶意代码,请朋友们及时联系我。十分感谢! Go语言第一课 Go语言进阶课 Go语言精进之路1 Go语言精进之路2 Go语言第一课 Go语言编程指南
商务合作请联系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
DigitalOcean Referral Badge

文章

评论

  • 正在加载...

分类

标签

归档



View My Stats