Thursday, November 10, 2011

Simple C++ UnitTests with QtCreator

I wanted to have some simple unit tests for my project to speed up testing and to cover corner cases. I'll outline here what I settled with. This is, at least for me, a suitable solution for a small free-time project. The goal was to have minimum hassle and modifications needed to the stock QtCreator created project, but still allowing me to save manual testing time with unit tests.

Here is the directory structure I used:

project.pro
 - src/main.cpp
 - src/module/implementation.cpp
 - src/module/implementation.h
 - tests/testmain.cpp
 - tests/module/testimplementation.cpp
 - tests/module/testimplementation.h

Optimally when developing and running the application, I'd want the workflow to be:

  • Compile the program. Continue if successful.
  • Compile the unit tests. Continue if successful.
  • Run the unit tests against the program
  • If unit tests fail, abort and display the errors
  • If unit tests succeed, create the application package (but without the unit test code), deploy it and run)
Qt has a pretty nice unit test framework, QTest. Unfortunately creating the workflow described above doesn't seem to be that straightforward when using QtCreator and qmake. Basically, from what I have gathered, you would need to divide your project to sub-projects where the main application code you want to test is built into a library and then different sub-projects for the proper application and the application doing the unit tests, each of which use the library. I'd like to hear if someone has an easier setup with examples for Harmattan applications?

I didn't want to use that much time into setting up the project and working out the kinks, so I settled for something simpler. If I want to run unit tests, I compile the application and the unit test files, but I exclude the application's main.cpp. Instead I include testmain.cpp which creates the unit test cases to run. If I want to run the application properly I just compile the application files without the unit test files. Whether to run the unit tests version is controlled from the project file with one CONFIG line. This removes some of the advantages of unit tests, as I have to remember to run the unit tests myself every once in a while to make sure nothing broke. But for me it's a good compromise for small and simple projects as I can use the project pretty much as it was created by QtCreator.

In my project.pro I have a line to switch between running unit tests or the proper application. This switch actually instructs the build system to include Qt Testing Library, but I use it as a trigger to switch between the unit test version and normal application version.

# Uncomment if just unit tests are to be ran.
# CONFIG += qtestlib
To run the unit tests instead of the main application, add after OTHER_FILES in project.pro rules to remove the main application's main.cpp and instead include the unit test's testmain.cpp. Also, the unit test sources are added here. Here's example from above directory structure:
qtestlib {
    # If qtestlib is in CONFIG, replace application's main.cpp
    # with unit test main. Also add unit test sources.
    SOURCES -= src/main.cpp
    SOURCES += tests/testmain.cpp \
        tests/module/testimplementation.cpp
    HEADERS += tests/logic/testimplementation.h
}
The default QtTest framework is a bit cumbersome in my opinion. It basically assumes that you have one executable for each test. I like to run all tests from the same tester. So that's why I have the testmain.cpp there. It contains the code to run all the tests. In the example it contains only the code to test the class "Implementation", but it's easy to add more tests there. Here's an example of it:

tests/testmain.cpp:

#include 
#include "module/testimplementation.h"

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);

    int retval(0);

    retval += QTest::qExec(&TestImplementation(), argc, argv);
    // Add more test like above here

    return (retval ? 1 : 0);
}
A skeleton of the TestImplementation class that tests the Implementation class could be like: tests/module/testimplementation.h
#ifndef TESTIMPLEMENTATION_H
#define TESTIMPLEMENTATION_H

#include 

#include "../../src/module/implementation.h"

class TestImplementation : public QObject
{
    Q_OBJECT

private slots:
    void dummyTest();
};

#endif // TESTIMPLEMENTATION_H
tests/module/testimplementation.h
void
TestImplementation::dummyTest() {
    // Always succeeds
    QVERIFY(true);
}

Final notes

As you may read between the lines, I'm not totally satisfied with this solution. So if you have concrete examples of better solutions, please post in the comments. But if you just want something simple for a small project, hopefully this will help get you started.

Further reading

Here's some sources I used, see them for more information and ideas for more advanced methods for unit testing with Qt:

Tuesday, November 1, 2011

QmlApplicationViewer and setContextProperty

Qt SDK 1.1.3 application wizard creates a nice stub for starting the application, QmlApplicationViewer. It takes care of some of the differences in starting the application in different environments like Desktop, Symbian and Harmattan. It also handles Harmattan booster for improving application start-up time and sets-up QML debugging.

So using it, and even transferring an existing application to using it, is worth to do.

Unfortunately, the stub QmlApplicationViewer created by QtCreator does not work correctly in Harmattan if you want to set context properties to access C++ functionality from QML. Here's how I got it working without breaking the application in Desktop and Harmattan environments.

First of all, here's the basic skeleton program created by QtCreator where I want "myContextProperty" to be accessible from QML:

main.cpp:

#include <QtGui/QApplication>
#include <QtDeclarative>

#include "qmlapplicationviewer.h"

Q_DECL_EXPORT int main(int argc, char *argv[])
{
    QScopedPointer<QApplication> 
       app(createApplication(argc, argv));
    QScopedPointer<QmlApplicationViewer> 
       viewer(QmlApplicationViewer::create());

    // Here you create "myContextProperty" that 
    // can be assigned as context property for QML.
    // See Qt documents or my earlier postings 
    // for examples.
    // 
    // After that, assign it as context property:

    QDeclarativeContext *ctxt = viewer->rootContext();
    ctxt->setContextProperty("myContextProperty", 
                             &myContextProperty);

    viewer->setOrientation(QmlApplicationViewer::ScreenOrientationAuto);
    viewer->setMainQmlFile(QLatin1String("qml/main.qml"));
    viewer->showExpanded();

    return app->exec();
}
This is how it should work out of the box. But not with the QmlApplicationViewer created by the current QtQuick application wizard. You can see the problem yourself from the source of QmlApplicationViewer, but the basic gist of the issue is that for Harmattan the QmlApplicationViewer::create() constructs a view from MDeclarativeCache, whose root context can not be accessed from QmlApplicationViewer. So above, you are setting a context properties for something that's not really used in QML.

To fix this, we add a function to QmlApplicationViewer to access it.

qmlapplicationviewer/qmlapplicationviewer.h:

public:
    QDeclarativeContext *rootContext();

qmlapplicationviewer/qmlapplicationviewer.cpp:

QDeclarativeContext *QmlApplicationViewer::rootContext()
{
    return d->view->rootContext();
}
Note that since qmlapplicationviewer is created by the wizard, the comments in it quite correctly warn against modifying. So when updating the SDK or QtCreator, check if some automatic updates happened add/remove above code if necessary.

One more thing... the working of the new qmlapplicationviewer also depends on updating the project file. There's some nice things anyway compared to the previous SDK, so well worth the effort in my opinion.