我陷入了困境,似乎无法找到解决方案。
我正在使用VS2005 SP1来编译代码。
我有一个全局功能:
A* foo();
我有一个模拟课
class MockA : public A {
public:
MOCK_METHOD0 (bar, bool());
...
};
在消息来源中,它的访问方式如下:foo()->bar()
。我无法找到模仿这种行为的方法。我无法改变消息来源,因此谷歌模拟烹饪书中的解决方案是不可能的。
任何有关正确方向的帮助或指示都将受到高度赞赏。 :)
答案 0 :(得分:15)
如果不更改源代码,或者使用与可执行代码链接的foo()
版本的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);
}
};
,则不可行。
来自GoogleMock's FAQ它说
我的代码调用静态/全局函数。我可以嘲笑它吗?
你可以,但你需要做一些改变。
通常,如果您发现自己需要模拟静态函数,则表明您的模块耦合得太紧(并且灵活性较低,可重用性较低,可测试性较差等)。你可能最好定义一个小接口并通过该接口调用该函数,然后可以很容易地模拟它。它最初有点工作,但通常会很快收回成本。
此Google测试博客post说得非常好。看看吧。
同样来自Cookbook
模拟免费功能
可以使用Google Mock来模拟自由函数(即C风格函数或静态方法)。您只需要重写代码以使用接口(抽象类)。
不是直接调用自由函数(比如OpenFile),而是为它引入一个接口,并有一个调用自由函数的具体子类:
foo()
您的代码应与FileInterface通信以打开文件。现在很容易嘲笑这个功能。
这可能看起来很麻烦,但实际上你经常会有多个相关的函数可以放在同一个界面中,所以每个函数的语法开销会低得多。
如果您担心虚拟功能带来的性能开销,并且性能分析确认您的问题,您可以将其与用于模拟非虚拟方法的配方相结合。
正如您在评论中提到的那样,您实际上提供了自己的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());
// ...
}
版本,您可以轻松解决此问题,其中包含另一个模拟类的全局实例:
{{1}}
在每个测试用例运行后,不要忘记清除所有通话期望。
答案 1 :(得分:1)
当然,根据GTest / GMock的文档解释解决方案的答案可能不太正确。
但我想添加一个临时的快速和肮脏的方法。它应该适用于您希望尽可能快速且非侵入地获得测试中的旧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 / GMock - 你需要自己将它添加到测试目标。
可能性相当有限,但你也可以在问题的例子中为返回类型构建一些有用的东西{/ 1}}。
不幸的是,如果不更改代码,这也不是解决方案。 如果确实有必要,可以谷歌进行“链接接缝”。但我的猜测是,这在练习中可能会非常麻烦。在很多/大多数情况下甚至可能根本不可能做到这一点?!
答案 2 :(得分:0)
有2个选项:
如果您坚持使用gmock,则优先级中有一个用于全局模拟的“扩展名”:https://github.com/apriorit/gmock-global
但是,它非常有限-至少我无法在5分钟内弄清楚如何对模拟通话产生副作用。
如果您愿意从gmock切换,那么河马有一种非常整洁的方式来做您想要的事情。
这是一个模拟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作为测试框架,但是我认为河马也可以与Google的Testing框架一起使用(顺便说一句,我建议catch2真的很容易使用)。
答案 3 :(得分:0)
对我有用的是
A* foo()
中定义foo.cpp
,foo.cpp
包括在测试项目中,mock-foo.cpp
,该文件提供A* foo()
的模拟实现。例如,主项目文件的伪代码(例如.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()
简单而甜蜜,但在您的情况下可能奏效或不奏效。
答案 4 :(得分:0)
如果您的自由函数采用 std::function
对象的形式,您可以使用 MockFunction
模拟它。见this answer