【问题标题】:GoogleMock mocking non-virtual functionsGoogleMock 模拟非虚函数
【发布时间】:2018-07-18 17:48:35
【问题描述】:

我们正在为现有代码库编写单元测试。我们使用 Google Test/Google Mock 进行测试,C++11 和 Eclipse CDT 与 gcc 编译器。

我们的一个类聚合了一个 Boost Socket。它使用它作为实例,但幸运的是我们可以修改现有的代码库,我将其更改为指针并将套接字作为依赖项注入。所以我开始模拟对套接字的调用,但有一个问题:Boost 函数是非虚拟的。

我找到了说明如何使用 hi-perf dependency injection 模拟非虚拟函数的文档。但是,尝试如图所示实现它是不成功的。例如,文档说“模板化你的代码”。所以对于我们使用升压插座的班级,我遵循了他们的例子。我插入:

template <class boost::asio::ip::udp::socket>

这应该让我们插入我们的模拟类而不是具体的 Boost 类。我在上课之前和在接受套接字的构造函数之前分别尝试过,在头文件和实现文件中。在每个地方,我都会遇到大量错误,其中大部分是“没有匹配的函数调用”构造函数调用。

很明显,我做错了什么。有人知道某处的完整示例吗?

更新: 根据请求,这是我们目前拥有的:

GoogleMock 模拟非虚拟函数

class PIngester : public IPIngester{
public:
    // this is the method that takes the socket. It is the constructor, and the socket
    // is a default parameter so the existing code will still work. We added the socket 
    // as a parameter specifically for unit testing. If no socket is provided, the 
    // constructor creates one. It only will ever create a concrete Boost
    // Socket, not a mock one.
    PIngester(boost::asio::io_service& ioService, Band band,
              std::unique_ptr<boost::asio::ip::udp::socket> socket = std::unique_ptr<boost::asio::ip::udp::socket>(nullptr));
    ...

更新 2

我为模板定义了一个通用类类型,但这破坏了现有代码。这是我当前的版本:

class PIngester : public IPIngester{
public:
    template <class Socket>
    PIngester(boost::asio::io_service& ioService, Band band,
              std::unique_ptr<Socket> socket = std::unique_ptr<Socket>(nullptr));
    ...

我认为这可能是默认参数,但我不能确定。错误消息不是很有帮助:

 error: no matching function for call to ‘foonamespace::PIngester::PIngester(boost::asio::io_service&, foonamespace::Band&)’                                     
          new PIngester(ioService, band));

此错误消息来自现有代码;它似乎无法识别默认参数。

更新 3

我已经放弃了这种方法,而是决定编写一个 Boost Socket Wrapper。包装器将保存实际的 Socket 实例,其方法将直接传递到实际的 Socket。包装器的功能将是虚拟的,我的模拟对象将从包装器继承。然后我的模拟对象将模拟包装器的虚函数。

【问题讨论】:

  • 您能告诉我们到目前为止您是如何设置它的吗?对我来说,template &lt;class boost::asio::ip::udp::socket&gt; 似乎您将模板参数定义为函数定义的一部分(在这种情况下,您将定义一个通用类名而不是 boost::asio::ip::udp::socket)。如果您尝试根据 Google Mock 文档传递模板参数,我希望看到类似 TestFunction&lt;boost::asio::ip::udp::socket&gt;() 的内容。
  • 好的,已添加。如果我理解正确,如果我需要一种在生产代码中使用具体类和在测试中使用模拟类的方法,我只需要对其进行模板化吗?我不认为这会是一个问题,因为我只在没有传入套接字时在构造函数中实例化具体类,而生产代码已经这样做了。我总是会通过模拟测试。

标签: c++ unit-testing c++11 googlemock


【解决方案1】:

正如您所注意到的,问题在于编译器无法推断出 Socket 在此处表示的任何类型:

class PIngester : public IPIngester{
public:
    template <class Socket>
    PIngester(boost::asio::io_service& ioService, Band band,
              std::unique_ptr<Socket> socket = std::unique_ptr<Socket>(nullptr));
    ...

如果您尝试构造此类的对象而不指定第三个参数(如new PIngester(ioService, band)),那么Socket 究竟会是什么?

现在,在调用构造函数时无法显式指定任何模板参数,因此如果构造函数被模板化,则无法执行new PIngester&lt;boost::asio::ip::udp::socket&gt;(ioService, band) 之类的操作。


这里有几种解决方法(可能有很多方法):

  1. 您可以模板化 PIngester 类本身:

    template <class Socket>
    class PIngester : public IPIngester{
    public:
        PIngester(boost::asio::io_service& ioService, Band band,
                  std::unique_ptr<Socket> socket = std::unique_ptr<Socket>(nullptr));
        ...
    

    然后调用new PIngester&lt;boost::asio::ip::udp::socket&gt;(ioService, band)(或你的模拟类代替boost::asio::ip::udp::socket)就可以了。

  2. 你可以定义一个默认的模板参数:

    class PIngester : public IPIngester{
    public:
        template <class Socket = boost::asio::ip::udp::socket>
        PIngester(boost::asio::io_service& ioService, Band band,
                  std::unique_ptr<Socket> socket = std::unique_ptr<Socket>(nullptr));
        ...
    

    那么PIngester(ioService, band) 将始终使用这个默认的 Boost 类,如果你想在测试中使用它,你需要显式地传入一些 socket,它代表一个指向你的模拟类的唯一指针。

【讨论】:

  • 这些都是很好的建议,谢谢!我会给他们一个机会。
  • 我试过这个,但是像九头蛇一样,我砍掉了一个头,又出现了两个。现在 gcc 说我不能将模板类型作为成员数据。没关系;我将编写一个 Boost Socket 包装器,它具有直接传递到实际 Boost Socket 的虚拟函数。我的模拟对象将从包装器继承,并模拟它的虚函数。
  • 不错的主意;这是处理 3rd 方类的好方法。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-07-25
  • 2012-02-12
  • 2020-07-02
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多