在MSVC中模拟C函数(Visual Studio)
我正在阅读几篇关于模拟 C函数的文章(比如CMock或CMocka ),但我不确定在这个过程中如何用模拟函数替换实际函数。 例如,CMocka依赖于使用GNU编译器的自动换行,GNU编译器支持诸如--wrap
参数将__wrap
前缀附加到函数调用,或者弱符号允许您覆盖任何您喜欢的符号。
但是你如何在Visual Studio中为几乎所有其他框架做到这一点?
例如, CMock有一个与此类似的例子 (这里简化了很多):
// myfunc.c #include // this is the function we would like to test int MyFunc(char* Command) { // this is the call to the function we will mock return ParseStuff(Command); }
还有实际的实现,它包含链接器在实际应用程序中应该找到的实际function:
// parsestuff.c int ParseStuff(char* cmd) { // do some actual work return 42; }
现在,在测试期间,Ruby脚本创建了类似的模拟函数:
// MockParseStuff.c (auto created by cmock) int ParseStuff(char* Cmd); void ParseStuff_ExpectAndReturn(char* Cmd, int toReturn);
-
但是如果VS项目已经包含了
parsestuff.c
,那么来自myfunc.c
的调用怎么可能在myfunc.c
结束呢? -
这是否意味着我不能将
parsestuff.c
包含在unit testing项目中? 但是如果是这种情况,那么在任何测试中也不可能从myfunc.c
中模拟MyFunc
,因为我已经必须包含它来测试它?
(更新)我也知道我可以包含.c
文件而不是.h
文件,然后做一些预处理器来替换原始调用,如:
// replace ParseStuff with ParseStuff_wrap #define ParseStuff ParseStuff_wrap // include the source instead of the header #include #undef ParseStuff int ParseStuff_wrap(char* cmd) { // this will get called from MyFunc, // which is now statically included }
但这似乎很多管道,我甚至没有看到它提到任何地方。
这是一个简单而简短的hippomocks解决方案:
我创建了一个空的Win32控制台应用程序
- main.cpp中
- myfunc.c + myfunc.h
- parsestuff.c,parsestuff.h
并添加了示例中的代码。
在hippomocks的帮助下,您可以模拟每个C函数。 这是我的main.cpp的样子:
#include "stdafx.h" #include "myfunc.h" #include "hippomocks.h" extern "C" int ParseStuff(char* cmd); int _tmain(int argc, _TCHAR* argv[]) { MockRepository mocks; mocks.ExpectCallFunc(ParseStuff).Return(4711); char buf[10] = ""; int result = MyFunc(buf); return result; //assert result is 4711 }
HippoMocks是一个免费,简单且非常强大的单头框架,可以在GitHub上下载。
希望我赢得了赏金:)
更新,它是如何工作的:
- HippoMocks获取指向ParseStuff的func指针
- HippoMocks构建一个指向具有相同签名和自己实现的模板函数的替换func指针。
- Hippomocks从内存中的函数调用序言中修补jmp操作码,以便它指向被替换的函数。
- 更改和内存补丁在调用或析构函数后释放。
这是我的机器上的样子:
@ILT+3080(_ParseStuff): 00D21C0D jmp HippoMocks::mockFuncs::static_expectation1<0,char *> (0D21DB1h)
如果在内存窗口中观察内存地址00D21C0D(可能因运行而异),您将看到,在调用ExpectCallFunc之后它会被修补。
我没有处理过C模拟库或Visual Studio,但我在自己的项目中已经考虑过这个问题。 Feathers书中建议将预处理器接缝或链接接缝作为处理此问题的工具。 您已经提到了预处理器接缝,因此我将重点关注链接接缝。
链接接缝要求模拟函数位于库中,并且模拟函数位于库中。 测试可以链接到模拟函数库,而目标应用程序可以链接到原始库。
当然,正如您所提到的,要模拟MyFunc(),您必须创建另一个库和一个单独的测试应用程序来链接它(或者在测试应用程序中动态加载和卸载库)。
这听起来很费力,这就是我拖延在我自己的应用程序中添加测试的原因!
希望这可以帮助!