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.
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 anIClientConnection
to theNetworkServer
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.