Dear Readers!

In a previous post I discussed how to set up a project in Qt Creator with unit testing the easy way, which involved having two completely separate projects (a qmake and a cmake managed) within the same project directory, which used the same sets of files to achieve the desired result: being able to build and run the app and tests separately. Although that approach actually worked pretty well, I wanted to have better integration of the tests within the IDE (test filtering, less screen clutter on successful tests, generally better overview, etc.), which is actually supported, but not the way I used the build tools. Also, as the project grows, it is advisable to break up the program into a relatively small executable and separate shared libs, but the previous setup didn’t have such a structuring either.

In order to be fix these issues and be able to access the integrated test features, I needed to migrate the entire project to a pure qmake setup, which in qt terminology means to have a subdir project. Here’s the intended project folder tree:

MyCoolProject
 |--MyCoolProj.pro
 |--MyCoolApp
    |--MyCoolApp.pro
    |--main.cpp
    |--mainwindow.h
    |--mainwindow.cpp
    |--awesomestuff.h
    \--awesomestuff.cpp
 |--MyCoolLibs
    |--MyCoolLibs.pro
    |--MyLib1
       |--MyLib1.pro
       |--mylib1decl.h
       |--mylib1.h
       \--mylib1.cpp
    \--MyLib2
       |--MyLib2.pro
       |--mylib2decl.h
       |--mylib2.h
       \--mylib2.cpp
  |--GoogleTest
     |--googletest
        |--ci
        |--googlemock
        |--googletest
        |-- ...
        \--WORKSPACE
     |--GoogleTest.pri
     \--GoogleTest.pro
  \--Tests
     |--Tests.pro
     |--main.cpp
     |--tst_mylib1.cpp
     \--tst_mylib2.cpp
     

For this, we need a global, top level project file using the subdirs template:

qmake (MyCoolProj.pro)

1
2
3
4
5
6
7
8
TEMPLATE = subdirs

SUBDIRS += MyCoolApp \
           MyCoolLibs \
           GoogleTest \
           Tests
           
           

It is rather clear, that we have 4 projects, but what is GoogleTest doing there one might ask? Well, some more experienced people say that it is highly advisable to have the vanilla GoogleTest source tree as part of the project, as it is rather sensitive if it wasn’t build using exactly the same tools and settings as the project being tested. Let’s move on to the MyCoolProg setup:

qmake (MyCoolApp.pro)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
QT += core gui
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets

TARGET = MyCoolApp
TEMPLATE = app

DEFINES += QT_DEPRECATED_WARNINGS

CONFIG += c++14

DEPENDPATH += ../MyCoolLibs/MyLib1 \
              ../MyCoolLibs/MyLib2

INCLUDEPATH += ../MyCoolLibs/MyLib1 \
               ../MyCoolLibs/MyLib2

LIBS += -L../MyCoolLibs/MyLib1 -lMyLib1 \
        -L../MyCoolLibs/MyLib2 -lMyLib2

HEADERS += mainwindow.h \
           awesomestuff.h

SOURCES += main.cpp \
           mainwindow.cpp \
           awesomestuff.cpp
           
FORMS +=   mainwindow.ui

There is nothing fancy here, it is your average app template having the usual contents. Let’s move on the interesting stuff, the libs and tests setup. Since we have multiple libraries, it is a good idea to build them separately, which again can be achieved via using the subdirs template:

qmake (MyCoolLibs.pro)

1
2
3
4
TEMPLATE = subdirs

SUBDIRS += MyLib1 \
           MyLib2

qmake (MyLib1.pro)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
QT += core

TARGET = MyLib1
TEMPLATE = lib

CONFIG += c++14
CONFIG -= debug_and_release

DEFINES += QT_DEPRECATED_WARNINGS
DEFINES += MYLIB1_LIBRARY

HEADERS += \
    mylib1decl.h \
    mylib1.h \

SOURCES += \
    mylib1.cpp \
    

As you can see the library declaration is a standard one, again nothing special here. One must also remember to define Q_DECL_EXPORT and Q_DECL_IMPORT in the *decl.h file. See official documentation for more info on how to create libraries. Anyway, in case of this project, the decl.h file looks like this:

C++

1
2
3
4
5
6
7
8
9
10
11
12
#ifndef MYLIB1DECL_H
#define MYLIB1DECL_H

#include <QtCore/qglobal.h>

#if defined(MYLIB1_LIBRARY)
#  define MYLIB1_EXPORT Q_DECL_EXPORT
#else
#  define MYLIB1_EXPORT Q_DECL_IMPORT
#endif

#endif // MYLIB1DECL_H

GoogleTest is also compiled as a library and this is the .pro file for it:

qmake (GoogleTest.pro)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
TEMPLATE = lib
CONFIG += c++14
CONFIG += static exceptions
CONFIG -= debug_and_release

TARGET = GoogleTest

INCLUDEPATH += \
    googletest/googletest/include \
    googletest/googlemock/include \
    googletest/googletest \
    googletest/googlemock

SOURCES = \
    googletest/googletest/src/gtest-all.cc \
    googletest/googlemock/src/gmock-all.cc

An additional .pri file makes including google test into the test project a bit easier (as you’ll see shortly):

qmake (GoogleTest.pri)

1
2
3
4
5
INCLUDEPATH += \
    $$PWD/googletest/googletest/include \
    $$PWD/googletest/googlemock/include

LIBS += -L../GoogleTest -lGoogleTest

And finally here comes the part that is harder to get right. The test executable is a separate app, so if you press the run button in Creator, only the your normal app should start, but not the tests. If you want to execute the tests, you need to go to the “Test results” tab of Creator.

qmake (Tests.pro)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
QT += testlib         #Include QtTest to use SignalSpy, QTest::mouseClick, etc

TARGET = Tests
TEMPLATE = app

CONFIG += c++14
CONFIG += testcase    #Creates 'check' target in Makefile.
CONFIG -= debug_and_release
CONFIG += cmdline

INCLUDEPATH += ../MyCoolLibs/MyLib1 \
               ../MyCoolLibs/MyLib2

LIBS += -L../MyCoolLibs/MyLib1 -lMyLib1 \
        -L../MyCoolLibs/MyLib2 -lMyLib2

include(../GoogleTest/GoogleTest.pri)

HEADERS += 


SOURCES += main.cpp \
           tst_mylib1.cpp \
           tst_mylib2.cpp

As you can see, the template is still an app, but the testcase has been added to the config parameters as well as the previously shown GoogleTest.pri has also been included. This can be helpful when migrating from one platform/system to another, as this way we don’t need to touch the project files to tell qmake where google test is.

That is it. With the above presented setup one can create two separate executables (test and main app), both use the same compiled libs, the play button in Creator executes the main app, while the tests appear in the “Test results” tab and test selection also works. It is also important to mention, that for some reason Qt Creator doesn’t always update the LD_LIBRARY_PATH environment variable properly, so the application might not start, while spitting out the following message:

Shell

libMyLib1.so.1: cannot open shared object file: No such file or directory

In this case go to

Projects (mode) –> Run Settings (under Build & Run) –> Run Environment (expand the Details) –> LD_LIBRARY_PATH (somewhere in the list)

in Creator and check if LD_LIBRARY_PATH really does include the path to the directory the libraries were compiled into. If not, simply update the env var and the app will start to work.

That’s all for today, as always, thanks for reading.