Add new comment

Qt Test limitations and workarounds

Pros and cons

Why use Qt Test for unit testing and TDD (test-driven development)?

  • small testing framework (about 6K lines of source code);
  • seamless integration with QtCreator development environment;
  • full support of Qt framework including classes/types and signal/slots;
  • simple to start and use.

However, there are some drawbacks, too.

  • designed as "single testing class per test project"; Qt developers use test project hierarchy themselves with SUBDIRS;
    TEMPLATE = subdirs
    SUBDIRS += \
        test_of_unit1 \
        test_of_unit2 \
        ...
  • no mocks;
  • checking functions/macros doesn't support some standard types i.e. wide strings and literals;
  • Qt dependency: a test executable requires some DLL to run (QtTest and QtCore at least, static linking is allowed in commercial version only).

Some of these limitation may be removed using the following workarounds.

Multiple Qt testing classes in a single project

Create test project and add new testing classes as it described in the Qt manuals.

Remove any declarations like QTEST_MAIN(MyTest1) from testing class files.

You need to add the main C++ source file to the project, i.e. bootstrap.cpp. Include test class files to bootstrap.cpp and write the main function as following:

#include <QtTest>
// Testing classes
#include "MyTest1.h"
#include "MyTest2.h"
 
int main(int argc, char *argv[])
{
    int status = 0;
    QTest::setMainSourcePath(__FILE__, QT_TESTCASE_BUILDDIR);
    {
        MyTest1 tc;
        status |= QTest::qExec(&tc, argc, argv);
    }
    {
        MyTest2 tc;
        status |= QTest::qExec(&tc, argc, argv);
    }
    return status;
}

From now QtCreator test explorer shows all test results.

Testing strings

QCOMPARE doesn't seem to test wide-character literals L"Abc", std::wstring and even std::string values correctly, there is no additional information showing when a check is failed. Fortunately, you can add some simple macros to compare strings.

#define QSCOMPARE(string1, string2) {\
    QByteArray ba1 = QString::fromStdString(string1).toLocal8Bit(); \
    QByteArray ba2 = QString::fromStdString(string2).toLocal8Bit(); \
    const char *ps1 = ba1.data(), *ps2 = ba2.data(); \
    QTest::compare_helper(static_cast<bool>(string1 == string2), \
                          "Compared strings are not the same", \
                          QTest::toString(ps1), QTest::toString(ps2), \
                          #string1, #string2, \
                          __FILE__, __LINE__); \
}
//
#define QWSCOMPARE(wstring1, wstring2) {\
    QByteArray ba1 = QString::fromStdWString(wstring1).toUtf8(); \
    QByteArray ba2 = QString::fromStdWString(wstring2).toUtf8(); \
    const char *ps1 = ba1.data(), *ps2 = ba2.data(); \
    QTest::compare_helper(static_cast<bool>(wstring1 == wstring2), \
                          "Compared wide strings are not the same", \
                          QTest::toString(ps1), QTest::toString(ps2), \
                          #wstring1, #wstring2, \
                          __FILE__, __LINE__); \
}
//

Afterword

Being testing QtTest framework when migrating from Microsoft CppUnit one, I realized quickly these limitations. However, my project isn't based on Qt framework so I didn't have big advantages of using QtTest even after workarounds have been found. On the other hand QtTest is still a good choice when you develop Qt-based applications.