在 MSVC 中模拟 C 函数 (Visual Studio)
Mocking C functions in MSVC (Visual Studio)
我正在阅读几篇关于模拟 C 函数的文章(如 CMock, or CMocka),但我不确定在此过程中实际函数是如何被模拟函数替换的。例如,CMocka 依赖于使用 GNU 编译器的自动包装,它支持像 --wrap
这样的参数将 __wrap
前缀附加到函数调用,或 weak symbols 允许你可以覆盖任何你喜欢的符号。
但是对于几乎所有其他框架,您如何在 Visual Studio 中做到这一点?
比如CMock has an example类似这样(这里简化了很多):
// myfunc.c
#include <parsestuff.h>
// 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);
}
还有实际的实现,里面包含链接器在实际应用中应该找到的实际函数:
// 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
的调用在MockParseStuff.c
结束呢?
这是否意味着我不能将 parsestuff.c
包含在单元测试项目中?但如果是这种情况,那么也不可能在任何测试中从 myfunc.c
中模拟 MyFunc
,因为我已经必须包含该文件才能对其进行测试?
(更新) 我也知道我可以包含 .c
文件而不是 .h
文件,然后做一些预处理器的东西替换原来的调用,如:
// replace ParseStuff with ParseStuff_wrap
#define ParseStuff ParseStuff_wrap
// include the source instead of the header
#include <myfunc.c>
#undef ParseStuff
int ParseStuff_wrap(char* cmd)
{
// this will get called from MyFunc,
// which is now statically included
}
但这似乎有很多管道,我什至没有看到任何地方提到它。
我没有处理过 C 模拟库或 Visual Studio,但我在自己的项目中考虑过这一点。 Feathers book 建议预处理器接缝或 link 接缝作为处理此问题的工具。您已经提到了预处理器接缝,所以我将重点放在 link 接缝上。
link 接缝要求模拟函数在库中,模拟函数在库中。测试可以 link 针对模拟函数库,而目标应用程序可以 link 针对原始库。
当然,正如您提到的,要模拟 MyFunc(),您必须创建另一个库和一个单独的测试应用程序来 link 针对它(或在测试应用程序中动态加载和卸载库)。
这听起来很费力,这就是为什么我拖延在我自己的应用程序中添加测试!
希望对您有所帮助!
这是一个简单而简短的河马解决方案:
我使用
创建了一个空的 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 的函数指针
- HippoMocks 构建了一个替换函数指针,指向具有相同签名和自己实现的模板函数。
- Hippomocks 修补内存中函数调用序言中的 jmp 操作码,使其指向被替换的函数。
- 替换和内存补丁在调用后或在析构函数中释放。
这是它在我的机器上的样子:
@ILT+3080(_ParseStuff):
00D21C0D jmp HippoMocks::mockFuncs<char,int>::static_expectation1<0,char *> (0D21DB1h)
如果您观察内存地址 00D21C0D(可能与 运行 运行 不同)在内存 window 中,您会看到,它在调用 ExpectCallFunc 后得到了修补。
我正在阅读几篇关于模拟 C 函数的文章(如 CMock, or CMocka),但我不确定在此过程中实际函数是如何被模拟函数替换的。例如,CMocka 依赖于使用 GNU 编译器的自动包装,它支持像 --wrap
这样的参数将 __wrap
前缀附加到函数调用,或 weak symbols 允许你可以覆盖任何你喜欢的符号。
但是对于几乎所有其他框架,您如何在 Visual Studio 中做到这一点?
比如CMock has an example类似这样(这里简化了很多):
// myfunc.c
#include <parsestuff.h>
// 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);
}
还有实际的实现,里面包含链接器在实际应用中应该找到的实际函数:
// 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
的调用在MockParseStuff.c
结束呢?这是否意味着我不能将
parsestuff.c
包含在单元测试项目中?但如果是这种情况,那么也不可能在任何测试中从myfunc.c
中模拟MyFunc
,因为我已经必须包含该文件才能对其进行测试?
(更新) 我也知道我可以包含 .c
文件而不是 .h
文件,然后做一些预处理器的东西替换原来的调用,如:
// replace ParseStuff with ParseStuff_wrap
#define ParseStuff ParseStuff_wrap
// include the source instead of the header
#include <myfunc.c>
#undef ParseStuff
int ParseStuff_wrap(char* cmd)
{
// this will get called from MyFunc,
// which is now statically included
}
但这似乎有很多管道,我什至没有看到任何地方提到它。
我没有处理过 C 模拟库或 Visual Studio,但我在自己的项目中考虑过这一点。 Feathers book 建议预处理器接缝或 link 接缝作为处理此问题的工具。您已经提到了预处理器接缝,所以我将重点放在 link 接缝上。
link 接缝要求模拟函数在库中,模拟函数在库中。测试可以 link 针对模拟函数库,而目标应用程序可以 link 针对原始库。
当然,正如您提到的,要模拟 MyFunc(),您必须创建另一个库和一个单独的测试应用程序来 link 针对它(或在测试应用程序中动态加载和卸载库)。
这听起来很费力,这就是为什么我拖延在我自己的应用程序中添加测试!
希望对您有所帮助!
这是一个简单而简短的河马解决方案:
我使用
创建了一个空的 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 的函数指针
- HippoMocks 构建了一个替换函数指针,指向具有相同签名和自己实现的模板函数。
- Hippomocks 修补内存中函数调用序言中的 jmp 操作码,使其指向被替换的函数。
- 替换和内存补丁在调用后或在析构函数中释放。
这是它在我的机器上的样子:
@ILT+3080(_ParseStuff):
00D21C0D jmp HippoMocks::mockFuncs<char,int>::static_expectation1<0,char *> (0D21DB1h)
如果您观察内存地址 00D21C0D(可能与 运行 运行 不同)在内存 window 中,您会看到,它在调用 ExpectCallFunc 后得到了修补。