C++ Gmock - WillOnce - 除了返回一个值,你还能做任何事情吗?

问题描述

我正在学习 gmock 并想知道如何在调用其中一个模拟方法时让我的模拟对象执行某些操作。

我需要模拟一些使用 Qt 的接口,并且我试图让 Qt 和 Gmock 一起工作。

例如,我模拟了一个网络套接字接口类,通过基于构造函数的 DI 将其提供给网络客户端类,并且我想告诉模拟套接调用方法之一在连接完成时向客户端发出信号,以便假装建立了联系。

我看到有 WillOnce 并且它需要“动作”,但我正在阅读的文档并没有真正解释“动作”可以是什么类型的东西。所有的例子都只是简单地返回一些东西或增加一个全局变量。您可以调用属于您正在模拟的抽象类的方法吗?

如果没有,我看到您可以单独定义一个假对象并使其成为模拟的成员,然后委托,但这似乎是在每个需要不同行为的测试用例中投入大量工作.

  • 无论是否要在特定测试用例中使用它,您都必须在界面中定义每个纯虚拟
  • 似乎我将不得不创建不同的假货,因此当假货行为不同时,每个测试用例的本地模拟也不同 ... 例如,也许我想在一个测试用例 a 中打开信号,而不是在另一个测试用例中发出信号

这是一些示例代码

class MySocketInterface : QObject
{
    Q_OBJECT
    public:
   
       virtual void connect(std::string url) = 0;
       
    signal:    // Specific to Qt,but this is what I need to call
       // Notifies subscribers
       void connected();
 }

 class MyThingToTest
 {
 public:
     MyThingToTest(std::unique_ptr<MySocketInterface> && socket);
     void connect(std::string url);
     State getState() const;
 private:
     std::unique_ptr<MySocketInterface> m_socket;
     // Changes state to connected
     void onConnected();
 }

 Test1:
 ) Make a mock socket
 ) Give it to MyThingToTest
 ) Call MyThingToTest::connect
 ) mock socket needs to send notification faking connecting by calling `emit connected`
 ) Check if MyThingToTest is in connected state

 Test2:
 ) Make a mock socket
 ) Give it to MyThingToTest
 ) Call MyThingToTest::connect
 ) mock socket needs to not send notification,and 30 seconds needs to elapse.
 ) Check if MyThingToTest is in an error state

我想避免必须定义一个全新的假类和模拟,将每个单元测试用例委托给它,其中动作会有所不同。

请注意,我不是在寻找 QSignalSpy 或验证信号和插槽。我想验证对它们做出反应的类的功能,并让这些类使用模拟依赖项,这样我就不必通过真实网络进行交谈。

解决方法

假设您使用的是 Qt 5 或更高版本,您应该能够使用 InvokeWithoutArgs 来实现您正在寻找的结果。您的模拟对象应定义如下:

class MockMySocketInterface : public MySocketInterface
{
    Q_OBJECT
public:
    MockMySocketInterface() { }
    virtual ~MockMySocketInterface() { }

    MOCK_METHOD1(connect,void(std::string));
};

您的测试如下所示:

auto socket = std::make_unique<MockMySocketInterface>(); // Could also use NiceMock here.

// This could alternately be an EXPECT_CALL,but you would need to use WillOnce
// or WillRepeatedly instead of WillByDefault.
ON_CALL(*socket,connect(_))
    .WillByDefault(InvokeWithoutArgs(socket.get(),&MockMySocketInterface::connected));
MyThingToTest thing(socket);

// etc.