无模拟功能
Mocking free function
我陷入了一个问题,似乎找不到解决方案。
我正在使用VS2005 SP1来编译代码。
我有一个全局功能:
A* foo();
我有一个模拟课堂
class MockA : public A {
public:
MOCK_METHOD0 (bar, bool());
...
};
在源中,它的访问方式如下:foo()->bar()
。我找不到嘲笑这种行为的方法。我无法更改来源,所以谷歌模拟烹饪书中的解决方案是毫无疑问的。
如有任何帮助或指示,我们将不胜感激。:)
不,如果不更改源代码,或者不带上与可执行代码链接的foo()
版本,这是不可能的。
从GoogleMock的常见问题解答中可以看出
我的代码调用了一个静态/全局函数。我可以嘲笑它吗
你可以,但你需要做一些改变。一般来说,如果你发现自己需要模拟一个静态函数,这表明你的模块耦合过于紧密(灵活性较差、可重用性较差、可测试性较差等)。您最好定义一个小接口,并通过该接口调用函数,这样就可以很容易地进行模拟。最初这是一项艰巨的工作,但通常很快就会得到回报。
这篇谷歌测试博客文章说得很好。看看吧。
同样来自食谱
无模拟函数
可以使用Google Mock来模拟一个自由函数(即C风格的函数或静态方法)。您只需要重写代码就可以使用接口(抽象类)。与其直接调用自由函数(比如OpenFile),不如为其引入一个接口,并有一个调用自由函数的具体子类:
class FileInterface {
public:
...
virtual bool Open(const char* path, const char* mode) = 0;
};
class File : public FileInterface {
public:
...
virtual bool Open(const char* path, const char* mode) {
return OpenFile(path, mode);
}
};
您的代码应该与FileInterface对话以打开文件。现在很容易模拟出这个函数。
这看起来可能很麻烦,但在实践中,您经常有多个相关的函数可以放在同一个接口中,因此每个函数的语法开销会低得多。
如果您关心虚拟函数所带来的性能开销,并且评测证实了您的担忧,那么您可以将其与模拟非虚拟方法的配方结合起来。
正如您在评论中提到的那样,您实际上提供了自己版本的foo()
,您可以通过使用另一个模拟类的全局实例来轻松解决这个问题:
struct IFoo {
virtual A* foo() = 0;
virtual ~IFoo() {}
};
struct FooMock : public IFoo {
FooMock() {}
virtual ~FooMock() {}
MOCK_METHOD0(foo, A*());
};
FooMock fooMock;
// Your foo() implementation
A* foo() {
return fooMock.foo();
}
TEST(...) {
EXPECT_CALL(fooMock,foo())
.Times(1)
.WillOnceReturn(new MockA());
// ...
}
在每次测试用例运行后,不要忘记清除所有的调用期望值。
有两个选项:
如果你坚持使用gmock,那么apriorit为全局嘲讽提供了一个"扩展":https://github.com/apriorit/gmock-global
不过,这是相当有限的——或者至少我无法在5分钟内弄清楚如何对一个被嘲笑的电话产生副作用。
如果你愿意从gmock切换,那么hippomocks有一种非常巧妙的方式来做你想做的事情。
下面是一个模拟fopen、fclose和fgets测试成员函数的例子,该函数使用cstdio从文件中读取(流效率非常低):
TEST_CASE("Multi entry") {
std::vector<std::string> files{"Hello.mp3", "World.mp3"};
size_t entry_idx = 0;
MockRepository mocks;
mocks.OnCallFunc(fopen).Return(reinterpret_cast<FILE *>(1));
mocks.OnCallFunc(fgets).Do(
[&](char * buf, int n, FILE * f)->char *{
if (entry_idx < files.size())
{
strcpy(buf, files[entry_idx++].c_str());
return buf;
}
else
return 0;
}
);
mocks.OnCallFunc(fclose).Return(0);
FileExplorer file_explorer;
for (const auto &entry: files)
REQUIRE_THAT(file_explorer.next_file_name(), Equals(entry.c_str()));
REQUIRE_THAT(file_explorer.next_file_name(), Equals(""));
}
测试中的功能如下所示:
string FileExplorer::next_file_name() {
char entry[255];
if (fgets((char *)entry, 255, _sorted_entries_in_dir) == NULL)
return string();
_current_idx++;
if (_current_idx == _line_offsets.size())
_line_offsets.push_back(static_cast<unsigned>(char_traits<char>::length(entry)) + _line_offsets.back());
return string(entry);
}
我在这里使用catch2作为测试框架,但我认为hippomocks也可以与谷歌的测试框架配合使用(顺便说一句,我推荐catch2非常容易使用)。
当然,根据GTest/GLock的文档解释解决方案的答案再正确不过了。
但我想添加一个临时的快速&肮脏的做法。它应该适用于您希望尽可能快速、无入侵地测试遗留C/C++代码的情况。(只是为了尽快进行修复、重构和更适当的测试。)
因此,为了模拟一些要测试的代码中出现的免费函数void foo(int)
,您只需在源文件中进行以下调整:
#if TESTING
#define foo(param) // to nothing, so calls to that disappear
#endif
// ... code that calls foo stays untouched and could be tested
指示代码在测试中运行的宏TESTING
没有GTest/GLock,您需要自己将其添加到测试目标中。
可能性相当有限,但您也可以构造一些对返回类型有用的东西,如问题示例中的A*
。
不幸的是,如果不更改代码,这也不是一个解决方案。如果真的有必要,你可以在谷歌上搜索"链接接缝"。但我的猜测是,这在实践中可能会很麻烦。在许多/大多数情况下,这甚至可能根本不可能?!
如果您的自由函数是std::function
对象的形式,则可以使用MockFunction
对其进行模拟。请参阅此答案
对我有效的是
- 为了在主项目中的单独源文件CCD_ 10中定义CCD_
- 而不是将CCD_ 11包括在测试项目中
- 在提供CCD_ 13的模拟实现的测试项目中包括不同的源文件CCD_
例如,主项目文件(例如.vcxproj
或CMakeLists.txt
)的伪代码:
include src/foo.hpp # declare A* foo()
include src/foo.cpp # define A* foo()
以及测试项目文件:
include src/foo.hpp
include test/mock-foo.cpp # define mocked A* foo()
简单而甜蜜,但在你的情况下可能有效,也可能无效。
- 在执行其他功能的同时播放动画(LED矩阵和Arduino/ESP8266)
- 如何使用Google Mock来模拟gettimeofday()
- 多态性和功能结合
- 带内存和隔离功能的SQLite
- G锁定铸造到基础上会释放模拟行为
- 在CMakeLists.txt的安装功能中使用.cmake文件有什么用
- 类模板的成员功能的定义在单独的TU中完全专业化
- 有没有一种方法可以创建一个带有哈希表的数据库,该哈希表具有恒定时间查找功能
- 如何在C++中获得"静态纯虚拟"功能?
- 两个文件使用彼此的功能-如何解决
- 在C++中将Import模拟为来自python的功能
- 无法使用谷歌模拟模拟CDatabase打开功能
- 用参数作为指针的模拟功能
- 测试模拟功能的最佳方法
- 在模拟过程中更改FMI变量/功能
- 如何重复模拟相同的功能并检查每个调用中的参数
- 如何为多个模拟引脚编写功能?(阿杜伊诺)
- 无模拟功能
- 使用谷歌模拟模拟免费功能
- 是否有一些用于模拟Glib::D ispatcher的Boost功能