为一个咨询项目做准备,学习了几种对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等等, 这些函数都需要在假的库函数中进行实现, 开销不小。
看过《修改代码的艺术》这本书的人可能对上面的例子有些眼熟,不错,上面的方法和例子就是从这本书中来的。
这本书对于处理遗留代码提供了大量的方法,强烈推荐阅读!
分享到:
相关推荐
**单元测试**是软件开发过程中不可或缺的一个环节,它主要用于验证程序中的最小可测试单元,如函数、方法或类,是否按预期工作。`CUnit`是一个流行的选择,特别是在C语言项目中进行单元测试。本教程将通过一个小型...
3. **集成到测试框架**: 将生成的代码添加到你的测试项目中,与测试框架进行集成。 4. **编写测试用例**: 在测试用例中,你可以使用mock对象来替代真实的依赖,并设定期望的行为。 5. **运行测试**: 执行测试,...
单元测试是一种软件开发实践,旨在验证程序中的最小可测试单元(如函数、方法或对象)的行为是否符合预期。它可以帮助开发者在早期发现和修复错误,提高代码质量,并且在后续的修改中提供了一种验证原有功能未被破坏...
它可能包含了对特定功能的测试点,比如`testFunction1`和`testFunction2`,这两个函数分别针对项目中的某个特定功能进行单元测试。通过模拟项目中的其他依赖函数,确保`mytest`中的代码能够独立且准确地运行。 **六...
在Linux环境下,gMock是Google Mock框架的一个版本,它是Google Test的一部分,用于创建和使用模拟对象(mock objects)进行单元测试。gMock允许开发者定义预期的行为并检查方法调用,以此来验证代码的正确性。...
单元测试是软件开发过程中的关键环节,它允许开发者针对程序中的最小可测试单元(如函数或方法)进行验证。通过编写和执行单元测试,开发者可以尽早发现代码中的错误,提高代码质量,并且随着代码库的扩展,单元测试...
单元测试是针对程序中的最小可测试单元,如函数、方法或模块进行的测试。在Python中,我们可以使用内置的`unittest`模块来编写单元测试。每个测试用例都是一个独立的函数,用于验证特定功能的行为。通过断言(assert...
在C语言中,static关键字主要有两种用途:一种是在函数内部修饰变量,使变量具有静态生命周期;另一种是在文件级别修饰变量或函数,使其只在当前文件可见,这被称为internal linkage。而在C++中,static关键字除了...
这种方法特别适用于那些需要与外部系统交互的模块,通过伪对象可以模拟外部系统的响应,以便于对核心逻辑进行独立的测试。伪对象能够隔离外部依赖,使得测试更加可靠和专注。 接下来,文档中通过“计算个人所得税”...
在IT行业中,"嘲笑"可能是指一种编程方法或者测试策略,即模拟(Mocking)。在C语言这样的系统级编程语言中,由于其简洁性和低级特性,模拟技术并不常见,但我们可以探讨一下在更高级别的语言如C++或C#中的模拟概念...
1. **单元测试**:单元测试是对软件中的最小可测试单元进行检查和验证,对于C语言来说,这通常是一个函数。开发者通过编写测试用例来验证每个函数的功能是否正常,确保其在各种输入情况下都能按预期工作。 2. **...
如AR/ARM(可能指的是嵌入式系统)、C和Char类型(C语言的基础数据类型)、Python函数和实例、程序和编程语言的一般概念,以及Postman(一个API测试工具)和Python的mock测试。这些都属于更广泛的编程和软件开发领域...
你可以使用Google Test(GTest)这样的开源库来编写测试用例,对函数或方法进行独立验证,确保它们按照预期工作。GTest提供了丰富的断言来检查程序状态,并支持测试套件和测试用例的组织。 2. **集成测试**:除了...
"matlab集成c代码-impex-tools:FMIIMPEx工具"是一个专为MATLAB设计的开源工具,它旨在帮助用户方便地导入和导出模型,特别是与FMI(Functional Mock-up Interface)标准兼容的模型。FMI是一个开放标准,允许不同工具...
// 使用mock服务进行测试 } ``` 在"mygotest"这个项目中,我们可以期待找到这些关于Go语言测试、并发、依赖注入等实践的实例或说明。通过学习和理解这些内容,开发者可以更好地掌握Go语言的特性和最佳实践,从而...