Christopher Pisz
Christopher Pisz

Reputation: 4010

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.

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.

Upvotes: 0

Views: 790

Answers (1)

RA.
RA.

Reputation: 7777

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.

Upvotes: 1

Related Questions