`
ender
  • 浏览: 41893 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

对C语言的函数进行mock的3种方法

阅读更多

为一个咨询项目做准备,学习了几种对C语言函数进行mock的方法,由于C语言面向过程, 缺乏封装,继承,多态等面向对象语言的特性, 也无法利用面向对象中多态这个特性来实现mock,相对来说比java等OO的语言难于测试。 但也不是没有办法,先看例子:
    下面的代码中account_update函数使用了db_update这个函数,它会直接调用数据库,是个重量级的依赖。 为了对这段代码进行测试, 需要把db_update函数隔离,怎么处理?

#include <DFHLItem.h>
#include <DHLSRecord.h>
extern int db_update(int, struct DFHLItem *);

void account_update(
    int account_no, struct DHLSRecord *record, int activated)
{
    if (activated) {
        if (record->dateStamped && record->quantity > MAX_ITEMS) {
            db_update(account_no, record->item);
        } else {
            db_update(account_no, record->backup_item);
        }
    }
    db_update(MASTER_ACCOUNT, record->item);
}

方法一:利用C语言的预处理(在编译之前进行Mock)

先引入一个头文件:
#include <DFHLItem.h>
#include <DHLSRecord.h>

extern int db_update(int, struct DFHLItem *);

#include "localdefs.h"

void account_update(
    int account_no, struct DHLSRecord *record, int activated)
{
    if (activated) {
        if (record->dateStamped && record->quantity > MAX_ITEMS) {
            db_update(account_no, record->item);
        } else {
            db_update(account_no, record->backup_item);
        }
    }
    db_update(MASTER_ACCOUNT, record->item);
}

在该头文件中提供一个db_update的定义,注意,使用了#define把db_update展开为一段代码
#ifdef TESTING
...
struct DFHLItem *last_item = NULL;
int last_account_no = -1;
#define db_update(account_no,item)\
    {last_item = (item); last_account_no = (account_no);}
...
#endif
这样C语言编译器可以把所有的db_update都替换成{last_item = (item); last_account_no = (account_no);},  这段代码会记录下最后的item和account_no,可以供测试中的验证使用
使用宏就会丢失类型安全,如果逻辑复杂的话,很容易出错谨慎使用该方法。

方法二: 使用函数指针(编译期进行mock)

(1)首先写一个函数指针:        int (*db_update)(int, struct DFHLItem *)
(2)把原来的db_update 改名为 int  db_update_production(int, struct DFHLITem *)
(3) 编写一个mock实现             int db_update_mock(int, struct DFHLITem *)
(4) 最后使用条件编译来制定到底用哪个函数
#ifdef TESTING
   db_update = db_update_mock
#else
   db_update = db_update_production
#endif
该方法很灵活, 可以随意通过函数指针进行替换,还能兼顾类型安全 ,推荐使用。


方法三: 在编译之后 Link时候进行替换
这就需要编写包括db_update的库函数,在link的时候使用这个假的库函数。  当然Link出来的exe文件指示一个测试版本。
如果需要函数很多, 还有db_insert, db_delete等等, 这些函数都需要在假的库函数中进行实现, 开销不小。

 

看过《修改代码的艺术》这本书的人可能对上面的例子有些眼熟,不错,上面的方法和例子就是从这本书中来的。

这本书对于处理遗留代码提供了大量的方法,强烈推荐阅读!

0
1
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics