Using Mocks with Networking



  • I have the following system: 1. A 'Messenger' class which is a singleton with the method sendMessageToTcpServer(char const *) which passes a string through a tcp socket to a server running on a different machine. When the different machine sends a message to me over the socket, my messenger class emits a signal called "receivedCPPP(char const *)" with the string as an argument. I have a slot connected to that signal so I can handle it.

    What I want to do is mock the messenger so when I send a specific string, I can tell the mock to emit the signal with the expected response string as an argument without having the different machine attached.

    Here's the test code I have so far. All it does is pretends to make the tcp socket connection. It doesn't really test anything yet.

    class MockMessenger : public Messenger {
    public:
        MOCK_METHOD0(isConnected,bool());
        MOCK_METHOD0(connectToHostServer,void());
        MOCK_METHOD1(sendMessageToTcpServer,void(char const *));
    };
    
    class MockTcpSocket : public QTcpSocket {
    public:
        MOCK_METHOD1(waitForConnected,bool(int));
    };
    
    TEST(TestMessenger,checksConnect) {
        MockMessenger m;
        ON_CALL(m,isConnected()).WillByDefault(Return(true));
    
        EXPECT_CALL(m,isConnected()).Times(1);
        Communicator::Instance()->connectToTcpServer();
        ASSERT_TRUE(m.isConnected());
    }
    
    TEST(TestMessenger,checksTcp) {
        MockTcpSocket s;
        ON_CALL(s,waitForConnected(5000)).WillByDefault(Return(true));
        EXPECT_CALL(s,waitForConnected(5000)).Times(1);
        ASSERT_TRUE(s.waitForConnected(5000));
    }
    
    
    
    int main(int argc, char** argv) {
        QTcpSocket s;
      // The following line must be executed to initialize Google Mock
      // (and Google Test) before running the tests.
      ::testing::InitGoogleTest(&argc, argv);
      ::testing::InitGoogleMock(&argc, argv);
      return RUN_ALL_TESTS();
    }
    
    

    I'm clueless as to how I can make a test suite and be independent of the attached system my code relies on. What can I do to make a test that will let me send a message like "{"mtype":"GIRQ"}" and receive the signal receivedCPPP("Version 1") for instance. Seems like a perfect need for a mock but I'm new to mocks and stubs unfortunately and what I have found so far has not given me enough insight.

    Many thanks for any advice!
    Michael


  • Lifetime Qt Champion

    Hi,

    What exactly do you want to know about mocks ? How to implement them ? How to use them ?



  • I guess my confusion is regarding both implementing and using in this case. I want to mock my class so when I send a method a string, my test expects to get a signal with a different string returned. As an example, my class Communicator is a singleton and I send a string to the server like this.

    Communicator::Instance.sendMsgToTcpServer("{\"mtype\":\"GIRQ\"}");

    When this happens, I expect get a signal back from the class called

    receivedGIRQ("Version 1")

    This string, "Version 1", was received in real life by the Communicator class from a tcp socket connected to a real life server. In testing I don't want the server there but I want the mocked Communicator class to act as though it received the message from the server and send the signal whenever I pass it the first string.

    I have seen how to expect a specific number of calls, and specify a return value, but not how to expect a signal with a specific string whenever I call a specific method.

    If it helps, this is the header file for the singleton.

    //your code here
    #ifndef MESSAGER_H
    #define MESSAGER_H
    
    #include <QObject>
    #include "navmessage.h"
    #include <QTcpSocket>
    #include <QTimer>
    #include "singleton.h"
    #include "dialogsplash.h"
    #include "mainwindow.h"
    class MainWindow;
    
    class IMessenger : public QObject {
    public:
        virtual void sendMessageToTcpServer(char const *) = 0;
        virtual void connectToTcpServer() = 0;
        virtual bool isConnected() = 0;
    };
    
    
    class Messenger : public IMessenger
    {
        Q_OBJECT
    public:
        explicit Messenger();
        virtual void sendMessageToTcpServer(char const *) override;
        virtual void connectToTcpServer() override;
        virtual bool isConnected() override;
        virtual ~Messenger() {}
        QTcpSocket *tcpSocket;  // the preset server on the organ
        bool voicingLocked;
        void sendRequest(char const *);
        void connectSeqSignal(MainWindow *);
        void connectDivPistonSignal(MainWindow *);
        QString firstValid;
        QString strmsg,partial,complete;
    
    
    private:
        bool _connected;
        bool            retryPreset;                    //!< true if we should retry connecting to the preset server
        bool            retrySeq;                       //!< true if we should retry connecting to the seq server
        QTimer *        timer;                          //!< timer used for testing/dev't
        bool            hasValidMessages(QString);
        QString         popFirstValidMessage(QString);
    
        static Messenger *m_pInstance;
    signals:
        void receivedGIRP(QVariantMap map);
        void receivedCAPT(QVariantMap map);
        void receivedCPPP(QVariantMap map);
        void receivedGERR(QVariantMap map);
        void receivedSEQR(QVariantMap map);
        void receivedSSTA(QVariantMap map);
        void connectionClosed(QString);
        void messengerError(QString);
        void displayMessage(QString);
        void refreshGVSignal();
        void sequenceStepInfoSignal(QString);
        void sequenceDivisionPistonSignal(QString);
    
    public slots:
        void refreshGVSlot();
        void sequenceStepInfoSlot(QString);
        void sequenceDivisionPistonSlot(QString);
    
    private slots:
    
        //! parses incoming JSON messages from servers; distributes most messages to the active screen
        void readServer();
    
        //! print the error returned when server closes connection
        void connectionClosedByServer();
    
        //! timer callback used for debugging only
        void timeout();
    
        //! slot called on socket errors
        void error(QAbstractSocket::SocketError);
    
        //! slot used for retrying connection attempts (periodic retries while servers starting up)
        void retryConnect();
    
    public slots:
        //! called when server is connected; proceed to another connection, or switch to Splash screen.
        void hostConnected();
    
    private:
    
    };
    
    typedef Singleton<Messenger> Communicator;
    
    #endif // MESSAGER_H
    
    

  • Lifetime Qt Champion

    Then rather than a QTcpSocket you should have the generic QIODevice.

    Then you can write your MockDevice to answer what you expect and in your code you either set a QTcpSocket when connecting to your real server or your MockDevice if you are testing or just don't have that server available.



  • ok, i'll look into QIODevice, thanks!


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.