Search code examples
c++qtgooglemock

C++ Gmock - WillOnce - Can you do anything aside from return a value?


I am learning gmock and wondering how I can make my mock object do something when one of the mocked methods is called.

I need to mock some interfaces that make use of Qt and I am trying to get Qt and Gmock to work together.

For example, I mocked a network socket interface class, gave it to a network client class via constructor based DI, and I want to tell the mock socket to call one of its methods to signal the client when connection completes, in order to pretend a connection was made.

I see there is WillOnce and it takes and "action" but the documentation I am reading doesn't really explain what kinds of things an "action" can be. All the examples simply return something or increment a global variable. Can you call methods belonging to the abstract class you are mocking?

If not, I see you can separately define a fake object and make it a member of the mock, and then delegate, but that seems like a hell of a lot of work to put into every test case that wants a different behavior.

  • You have to define every single pure virtual in your interface whether you want to use it or not for that particular test case
  • It seems I will have to create different fakes and therefore different mocks local to each test case, when the fake behavior differs ... For example, maybe I want open to signal in one test case a and not to signal in another

Here is a bit of example code:

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

I want to prevent having to define an entirely new fake class and mock that delegates to it every single unit test case where actions would differ.

Please note, I am not looking for QSignalSpy or to verify signals and slots. I want to verify the functionality of my classes that react to them and have those classes use mock dependencies so I don't have to talk across the real network.


Solution

  • You should be able to use InvokeWithoutArgs to achieve the result that you are looking for, assuming that you are using Qt 5 or greater. Your mock object should be defined as follows:

    class MockMySocketInterface : public MySocketInterface
    {
        Q_OBJECT
    public:
        MockMySocketInterface() { }
        virtual ~MockMySocketInterface() { }
    
        MOCK_METHOD1(connect, void(std::string));
    };
    

    Your test would look something like the following:

    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.