Search code examples
qtqt5signals-slotsgoogletestqnetworkaccessmanager

Using Google Test with a QNetworkAccessManager never emits finished


I'm trying to create a test that will use a QNetworkAccessManager to talk with our RESTful api. All I want to do is grab a simple JSon object using a QNetworkAccessManager. My code looks like this:

Connection::Connection(QString const &username, QString const &password, QString const &url, QString const &api) : 
    _user(username), _pass(password), _url(url) {

    _manager = new QNetworkAccessManager(this);
    QObject::connect(_manager, SIGNAL(finished(QNetworkReply*)), this, SLOT(receiveReply(QNetworkReply*)));

    QNetworkRequest request;
    request.setUrl(QUrl(_url.append(api)));
    request.setRawHeader("Authorization", QString("Basic ").append(QString("%1:%2").arg(_user).arg(_pass)).toUtf8());
    request.setHeader(QNetworkRequest::ContentTypeHeader, "text/json");

    QNetworkReply *reply = _manager->get(request);
}
void Connection::Connection(QNetworkReply *reply) {
    //do some validation of the reply...
    _data = QJsonDocument::fromJson(reply->readAll());
}
QJsonDocument Connection::data() const {
    return _data;
}

...

#include <gtest/gtest.h>
#include "Connection.h"
#include <QApplication>
TEST(ConnectionTest, creation) {
    int argc = 0;
    char *argv = 0;
    QApplication a(argc, &argv);
    Connection *connect = new Connection("user","abc123", https://mytest.com/", "api/");
    connect->deleteLater();
    while (connect->data().isEmpty()) {
        //loop forever
    }
}

Originally I was testing by having Connection also be a QWidget. The finished signal would only show if I called connection->show(); Then I have to close the widget manually and the test completes. However, this isn't really useful for automated unit testing.

If I use the above while loop to wait until _data has been set to something then nothing ever happens with the QNetworkAccessManager and it loops forever.

Is there some call that needs to happen before anything will move forward? Why was ->show() 'working?'


Solution

  • You're not spinning the event loop, so don't expect anything asynchronous to work at all.

    You need to add a signal to the Connection class that indicates when it's done, and use that signal to quit the event loop:

    TEST(ConnectionTest, creation) {
        int argc = 0;
        char *argv = 0;
        QCoreApplication app(argc, &argv);
        // No need to allocate the connection on the heap!
        Connection connection("user", "abc123", "https://mytest.com/", "api/");
        QObject::connect(&connection, &Connection::finished, 
                         &app, &QCoreApplication::quit());
        app.exec();
    }
    

    You could add the QApplication instance as a member of the ConnectionTest class.

    There's also no need to double the number of heap allocations by having the QNetworkAccessManager * manager member. You can have it as a regular member:

    class Connection : public QObject {
      Q_OBJECT
      QNetworkAccessManager manager;
      Q_SLOT void receiveReply(QNetworkReply *) { 
        ...
        emit finished();
      }
    public:
      explicit
      Connection(const QString & user, const QString & password,
                 const QUrl & url, const QString & path, 
                 QObject * parent = 0) : QObject(parent) { ... }
      Q_SIGNAL void finished();
    };