关于c ++:如何模拟库调用?

How to mock library calls?

cpp的新手(Java家伙)。

我有第三方库,有方法sendMail(txt)。
我不想测试库。 我想测试我自己的方法,所以为了做到这一点,我需要模拟库调用。

我自己的方法看起来像这样:

1
2
3
4
5
6
7
8
9
#include"mailsender.h"

int run(txt){
    analysis(txt);
    ...
    ...
    int status =  sendMail(txt);//sendMail is a 3rd party library call. i need to mock it.its not part of the unit test
    return status;
}

在Java中,mailsender是接口,它被注入我的类,所以在测试的情况下我注入mock。
在模拟库调用的cpp中有什么好的做法?
我可以在一个类中包装第三方库调用并注入这个类,但我正在寻找更简单的东西和常见的实践(也许是ifndf)。

我熟悉googlemock。
googlemock允许我模拟课程。 我不知道如何在我测试的方法中模拟一个调用。


所以我假设你有一个'全局'函数,它在一个库中实现,你可以包含一个头文件(用于获取定义)和链接(用于获取实现)。

你显然需要用自己的库替换库的实现 - 一个"什么都没做",所以你可以通过两种方式来实现:

  • 您将.dll(或.so)替换为您自己的实现,该实现具有第三方库公开的所有方法。一旦你编写了所有第三方lib函数的新版本,这很容易,但将它们全部写出来可能会很痛苦。
  • 您暂时删除库,并将实现这些功能的.cpp源文件中的调用替换为库。所以你要在.cpp文件中创建自己的sendMail()函数,并将其包含在程序中而不是mailsender.h include中。

后者更容易,但您可能还需要修改程序以不与第三方库链接。这也可能需要更改#include,因为一些编译器(例如VC ++)允许您在源代码中嵌入链接器指令。如果您这样做,那么您将无法阻止链接器包含第三方库。

另一种选择是修改您的代码以使用对sendMail调用的不同调用,例如您自己实现的test__sendMail()。包装这是一个宏,有条件地包含您的或实际的函数调用,具体取决于您的构建选项。

如果这是一个c ++库,那么你可能可以使用像你习惯的模拟框架,但它听起来像是一个C库,它们只是提供了一个直接在代码中使用的函数列表。您可以将库包装在您自己的类中并使用它而不是直接调用第三方lib函数。

有一个C模拟框架列表。


这是一个老问题,已经选择了响应,但也许以下贡献可以帮助其他人。

第一解决方案

您仍然需要创建一个自定义库来重新定义函数,但是您不需要将Makefile更改为链接到您的"假库",只需使用LD_PRELOAD和伪库的路径,这将是第一个链接器将找到然后使用。

二解决方案

ld(GNU)链接器有一个选项--wrap,它允许您只使用用户提供的另一个函数包装一个函数。这样您就不必创建新的库/类来模拟行为

以下是手册页中的示例

--wrap=symbol
Use a wrapper function for symbol. Any undefined reference to symbol will be resolved to"__wrap_ symbol". Any undefined reference
to"__real_ symbol" will be resolved to symbol.

This can be used to provide a wrapper for a system function. The wrapper function should be called"__wrap_ symbol". If it wishes to
call the system function, it should call"__real_ symbol".

Here is a trivial example:

1
2
3
4
5
6
7
void *
__wrap_malloc (size_t c)
{
  printf ("malloc called with %zu
"
, c);
  return __real_malloc (c);
}

If you link other code with this file using --wrap malloc, then all calls to"malloc" will call the function"__wrap_malloc" instead.
The call to"__real_malloc" in"__wrap_malloc" will call the real
"malloc" function.

You may wish to provide a"__real_malloc" function as well, so that links without the --wrap option will succeed. If you do this, you
should not put the definition of"__real_malloc" in the same file as
"__wrap_malloc"; if you do, the assembler may resolve the call before
the linker has a chance to wrap it to"malloc".


免责声明:我写过ELFspy。

使用ELFspy,以下代码将允许您通过将替换为替代实现来伪造/模拟sendMail函数。

1
2
3
4
5
6
7
8
9
10
11
12
13
void yourSendMail(const char* txt) // ensure same signature as sendMail
{
  // your mocking code
}

int main(int argc, char** argv)
{
  spy::initialise(argc, argv);
  auto sendMail_hook = SPY(&sendMail); // grab a hook to sendMail
  // use hook to reroute all program calls to sendMail to yourSendMail
  auto sendMail_fake = spy::fake(sendMail_hook, &yourSendMail);
  // call run here..
}

您的程序必须使用与位置无关的代码(使用共享库构建)进行编译才能实现此目的。

进一步的例子在这里:
https://github.com/mollismerx/elfspy/wiki


虽然没有interface关键字,但您可以在C ++中使用抽象基类来实现类似的功能。

如果您使用的库没有这样的抽象,您可以将它包装在您自己的"界面"后面。如果您的代码将对象的构造与使用分开(例如通过IoC),您可以使用它来注入假的或使用Mocks:

https://stackoverflow.com/questions/38493/are-there-any-good-c-mock-object-frameworks