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:
For this, we need a global, top level project file using the subdirs template:
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:
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:
1 2 3 4 TEMPLATE = subdirs SUBDIRS += MyLib1 \ MyLib2
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:
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:
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):
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.
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:
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.