嘲弄自由功能

Muh*_*san 11 c++ tdd unit-testing googlemock

我陷入了一个问题,似乎无法找到解决方案.

我正在使用VS2005 SP1来编译代码.

我有一个全局功能:

A* foo();
Run Code Online (Sandbox Code Playgroud)

我有一个模拟课

class MockA : public A {
public:
    MOCK_METHOD0 (bar, bool());
    ...
};
Run Code Online (Sandbox Code Playgroud)

在消息来源中,它是这样访问:foo()->bar().我无法找到模仿这种行为的方法.我无法改变消息来源,因此谷歌模拟烹饪书中的解决方案是不可能的.

任何有关正确方向的帮助或指示都将受到高度赞赏.:)

πάν*_*ῥεῖ 16

不可能,如果不更改源代码,或者将自己的版本foo()与可执行代码链接在一起.


GoogleMock的常见问题,它说

我的代码调用静态/全局函数.我可以嘲笑它吗?

你可以,但你需要做一些改变.

通常,如果您发现自己需要模拟静态函数,则表明您的模块耦合得太紧(并且灵活性较低,可重用性较低,可测试性较差等).你可能最好定义一个小接口并通过该接口调用该函数,然后可以很容易地模拟它.这最初是一项工作,但通常会很快收回成本.

这篇Google测试博客文章非常出色.看看这个.

也来自食谱

嘲弄自由功能

可以使用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);
  }
};
Run Code Online (Sandbox Code Playgroud)

您的代码应与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());
    // ...
}
Run Code Online (Sandbox Code Playgroud)

在每个测试用例运行后,不要忘记清除所有呼叫期望.

  • @leemes 那么答案是否定的。 (3认同)

Mr.*_*pMe 5

有 2 个选项:

如果您坚持使用 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(""));
}
Run Code Online (Sandbox Code Playgroud)

被测试的函数如下所示:

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);
} 
Run Code Online (Sandbox Code Playgroud)

我在这里使用 catch2 作为测试框架,但我认为 hippomocks 也可以与 Google 的测试框架一起使用(顺便说一句,我推荐 catch2,非常容易使用)。