在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); 
  1. 但是如果VS项目已经包含了parsestuff.c ,那么来自myfunc.c的调用怎么可能在myfunc.c结束呢?

  2. 这是否意味着我不能将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上下载。

希望我赢得了赏金:)

更新,它是如何工作的:

  1. HippoMocks获取指向ParseStuff的func指针
  2. HippoMocks构建一个指向具有相同签名和自己实现的模板函数的替换func指针。
  3. Hippomocks从内存中的函数调用序言中修补jmp操作码,以便它指向被替换的函数。
  4. 更改和内存补丁在调用或析构函数后释放。

这是我的机器上的样子:

 @ILT+3080(_ParseStuff): 00D21C0D jmp HippoMocks::mockFuncs::static_expectation1<0,char *> (0D21DB1h) 

如果在内存窗口中观察内存地址00D21C0D(可能因运行而异),您将看到,在调用ExpectCallFunc之后它会被修补。

我没有处理过C模拟库或Visual Studio,但我在自己的项目中已经考虑过这个问题。 Feathers书中建议将预处理器接缝或链接接缝作为处理此问题的工具。 您已经提到了预处理器接缝,因此我将重点关注链接接缝。

链接接缝要求模拟函数位于库中,并且模拟函数位于库中。 测试可以链接到模拟函数库,而目标应用程序可以链接到原始库。

当然,正如您所提到的,要模拟MyFunc(),您必须创建另一个库和一个单独的测试应用程序来链接它(或者在测试应用程序中动态加载和卸载库)。

这听起来很费力,这就是我拖延在我自己的应用程序中添加测试的原因!

希望这可以帮助!