Using Mocks with Networking
-
wrote on 1 Aug 2017, 13:13 last edited by mstoth 8 Jan 2017, 13:39
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 -
Hi,
What exactly do you want to know about mocks ? How to implement them ? How to use them ?
-
wrote on 10 Aug 2017, 14:44 last edited by mstoth 8 Oct 2017, 14:46
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
-
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.
-
wrote on 11 Aug 2017, 12:41 last edited by
ok, i'll look into QIODevice, thanks!