5

cpp 新手(Java 人)。

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

我自己的方法是这样的:

#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 中,邮件发送器是接口,它被注入到我的类中,所以在测试的情况下我注入模拟。cpp模拟库调用的好习惯是什么?我可以将第 3 方库调用包装在一个类中并注入这个类,但我正在寻找更简单的东西和常见的做法(也许是 ifndf)。

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

4

4 回答 4

5

所以我假设你有一个在库中实现的“全局”函数,你都包含一个头文件(以获取定义)和链接(以获取实现)。

你显然需要用你自己的替换库的实现——一个“什么都不做”的,所以你可以通过两种方式做到这一点:

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

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

另一种选择是修改您的代码以对 sendMail 调用使用不同的调用,例如您自己实现的 test__sendMail()。Wrap this 是一个宏,可以根据您的构建选项有条件地包含您的或真实的函数调用。

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

有一个 C 模拟框架列表。

于 2012-07-27T12:20:01.587 回答
3

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

第一个解决方案

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

例子

第二种解决方案

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

这是手册页中的示例

--wrap=symbol 对符号使用包装函数。任何未定义的符号引用都将被解析为“__wrap_ symbol”。任何对“__real_ symbol”的未定义引用都将被解析为符号。

这可用于为系统函数提供包装器。包装函数应称为“__wrap_ symbol”。如果它想调用系统函数,它应该调用“__real_symbol”。

这是一个简单的例子:

void *
__wrap_malloc (size_t c)
{
  printf ("malloc called with %zu\n", c);
  return __real_malloc (c);
}

如果您使用 --wrap malloc 将其他代码与此文件链接,则所有对“malloc”的调用将改为调用函数“__wrap_malloc”。在“__wrap_malloc”中对“__real_malloc”的调用将调用真正的“malloc”函数。

您可能还希望提供“__real_malloc”函数,以便没有 --wrap 选项的链接会成功。如果这样做,则不应将“__real_malloc”的定义与“__wrap_malloc”放在同一个文件中;如果你这样做了,汇编器可能会在链接器有机会将它包装到“malloc”之前解决调用。

于 2015-04-21T10:49:10.223 回答
2

免责声明:我写了 ELFspy。

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

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

于 2018-06-17T18:29:57.463 回答
0

尽管没有interface关键字,但您可以在 C++ 中将抽象基类用于类似的事情。

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

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

于 2012-07-27T11:44:54.323 回答