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:

No comments:

Post a Comment