How to create dependency injection for testing QTcpSocket?

1.3k views Asked by At

I'm trying to write unit tests for a Qt5 application that I wrote, but I'm stumped about how to handle the classes that involve networking. My main class includes a QTcpServer subclass, which overrides QTcpServer::incomingConnection to create a ClientConnection object and hands it off to a thread:

void NetworkServer::incomingConnection(qintptr socketDescriptor)
{
    QThread* clientThread = new QThread();
    ClientConnection* clientConnection = new ClientConnection(socketDescriptor);
    clientConnection->moveToThread(clientThread);

    // connect signals & slots to ClientConnection (removed for clarity)

    connect(clientThread, &QThread::started, clientConnection, &ClientConnection::run);
    clientThread->start();
}

The ClientConnection class uses the socketDescriptor to open a new QTcpSocket in the dedicated thread, receives data from a client, and processes it.

ClientConnection::ClientConnection(int socketDescriptor, QObject *parent) :
    QObject(parent), socketDescriptor(socketDescriptor)
{
    tcpIncomingData = new QByteArray;
}

void ClientConnection::run()
{
    QTcpSocket socket;
    if(!socket.setSocketDescriptor(socketDescriptor)) {
           emit sig_error(socket.error());
           return;
       }

    if(socket.waitForReadyRead(5000)) {
        *tcpIncomingData = socket.readAll();
        qDebug() << "data received: " << tcpIncomingData;
    } else {
        qDebug() << "socket timed out!";
    }

    parseXmlData();

    socket.disconnectFromHost();
    socket.waitForDisconnected();
}

This class isn't finished but I want to start writing tests now. My issue is how to handle the socketDescriptor. I assume I need to use some sort of dependency injection, but I don't think that's feasible without creating an entire QTcpServer in the test case.

Testing network code must be common these days, so there must be a common way to handle this without including half of my application. This seems like a general question, but if more detail about my specific application is required please let me know.

1

There are 1 answers

1
Dmitry Ledentsov On

First of all, you might want to define clean interfaces (meaning pure abstract classes) for your classes, so that mocking and exchanging them wouldn't be a problem, say IClientConnection.

Then, you could declare your dependencies explicitly, i.e. either pass an IClientConnection factory or an instance of an IClientConnection to the NetworkServer constructor.

In your test, you can now implement a mock of IClientConnection and pass it or its factory (may also be an interface) to the constructor in the test. Perhaps, you might want to use a smart pointer (shared_ptr or something native to Qt), so that automatic resource deallocation would minimize bugs.

If your software grows and you find yourself writing a lot of manual dependency injection, you might use a DI library. I've recently started a review of some of the available C++ DI libraries.

Finally, you might get far by mocking your dependencies using a mocking framework, such as googlemock.