UPDATE: A newer post shows how to create a proper subdirs project with testing. Because of that, the majority of the content in this post is not valid any more.

Dear Readers,

In this post I’m going to share some of my findings about setting up a project with unit testing in Qt Creator. This might seem a trivial task to do from first glance, but unfortunately it turned out to be a bit more problematic than I thought. Let’s dive right in.

One of the first things that comes into mind when starting a new project is how to organize different source files. Obviously, based on some logic, the programmer will place them into various folders so that they don’t get all mixed up. In Qt Creator however, creating a new folder and putting some source files into that folder is not really straightforward for some reason. I’m not sure why such a design was chosen, but to place some new source files into a project subfolder, one must first create that subfolder with the help of a file manager, then manually type that folder name in Qt Creator’s ‘New File’ dialog right before the source and header file names. An example is depicted below:

nameAdding new files to a project subfolder in Qt Creator

As it is clearly seen above, this feature is a bit obscured, as there are no obvious ways to select any directory and there is no hint that typing the folder name will be handled correctly either. Fortunately though, the source and header files will be automatically included into the qmake project file as needed, and the they will appear in their appropriate subfolders in project explorer panel as well.

After getting such a triviality out of the way, we can now finally discuss how to create a project with unit tests. Since Qt Creator supports at least two build systems and two unit testing frameworks out of the box, the programmer is given some flexibility how to approach such a task. I will not explore all combinations and possibilities, but will only demonstrate some approaches I tried.

One of qmake’s features is that it allows the creation of a ‘Subdirs’ project, which basically means that there are several subfolders, each containing its own source files and qmake project files, which are then in turn managed by the master .pro file found in the root folder of the entire project. This allows the programmer to establish a more fine-grained control over how sources placed into different subfolders are built, and in fact this feature is actually quite similar (if not identical) to what CMake offers as well (though I’m not sure if CMake made this a special thing by naming it). From first glance, this might seem like the perfect setup to create a main and a test project, where the aforementioned flexibility can be used to build the different sources according to their purpose. An example file structure for such an arrangement can be seen below.

MyProject
 |--MyProject.pro
 |--ProjSources
 |  |--GUI
 |  |  |--guisrc.cpp
 |  |  |--guisrc.h
 |  |  \--guisrc.ui
 |  |--ProjSources.pro
 |  |--main.cpp
 |  |--mysource1.cpp
 |  |--mysource1.h
 |  |--mysource2.cpp
 |  \--mysource2.h
 \--ProjTests
    |--ProjTests.pro
    |--main.cpp
    |--tst_mysource1.cpp
    \--tst_mysource2.cpp

Unfortunately, despite this setup looking really promising, there was a problem I couldn’t fix, namely that qmake treated the entire project as a test project, regardless if only the test project was declared to be a test (.pro file contained “QT += testlib” and “CONFIG += testcase”). This meant that although I was able to compile the sources, the generated executable for the main program didn’t start except for the executable of the test. If I removed the tests from the entire project, the main program executable started working again. I’m not sure what caused the problem, maybe I simply overlooked something. Nevertheless, I was expecting this to work, since the .pro files were mostly auto-generated by Qt Creator and one would assume that such a setup was tested at Qt headquarters for ease of use.
NOTE: a solution to this problem has been found since, please check the link to the the new post at the bottom of the page

Anyway, since the online documentation wasn’t overly clear how to make such a setup work, I decided not to spend an excessive amount of time on trying to make this work, especially considering that qmake is in the process of being replaced by something else (qbs). Instead, I came up with an alternate approach that seemed not to compromise too much on the flexibility of the previous one, yet it was still mostly easy to configure. The idea was to create a main project (MyProject) first, then place the tests (ProjTests) into a subfolder of MyProject while it is being managed by a completely separete project file. This way all the sources and tests would be in the same root folder, and with a bit of tweaking of the test project file, both projects would be pointing to the same source files, which in turn allows simultaneous usage and development from whichever project. Here’s the new project tree:

 MyProject
 |--GUI
 |  |--guisrc.cpp
 |  |--guisrc.h
 |  \--guisrc.ui
 |--ProjTests
 |  |--CMakeLists.txt
 |  |--main.cpp
 |  |--tst_mysource1.cpp
 |  \--tst_mysource2.cpp
 |--MyProject.pro
 |--main.cpp
 |--mysource1.cpp
 |--mysource1.h
 |--mysource2.cpp
 \--mysource2.h

This works just fine, because qmake (or CMake for that matter) doesn’t treat any folder or file as part of the project unless it is explicitly specified in the .pro file, so having ProjTests as a subfolder of MyProject doesn’t pose a problem at all. An astute reader might have already spotted that I chose CMake to manage the test project, and there is a good reason for that: when new files are being added through the main project, the test project’s config file doesn’t get updated automatically. Since CMake doesn’t require an explicit mention of the header and .ui files (unlike qmake) in the config file, it is less of a burden to maintain it manually.

Apart from the above mentioned manual addition of the to be tested source files, the amount of manipulation one needs to do on CMakeLists.txt is surprisingly minimal: to make the example folder structure above work, one just needs to provide a proper path for the to be tested source files (which can be both an absolute and relative path) and insert it before the source file names in “add_executable()”. Adding the path to “include_directories()” doesn’t hurt either. If the user is careful and files are only added when the main project is active in the project explorer pane of Qt Creator, the qmake file doesn’t need manual editing at all.

The following CMake and qmake files show how to work with a file tree presented above, while both Qt and OpenCV frameworks are used by the project as well. Speaking of frameworks, it is really important to remember that since the test and main projects are handled entirely separately, the inclusion of additional frameworks in only one of the project files is not enough to make the compilation work in all cases. Let’s see the files:

Main project config file:

qmake

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
28
29
30
31
32
33
34
35
36
37
38
39
40
QT       += core gui

greaterThan(QT_MAJOR_VERSION, 4): QT += widgets

TARGET = MyProject
TEMPLATE = app

DEFINES += QT_DEPRECATED_WARNINGS

CONFIG += c++14

INCLUDEPATH += ./GUI

SOURCES += \
        main.cpp \
        GUI/guisrc.cpp \
        mysource1.cpp \
        mysource2.cpp

HEADERS += \
        GUI/guisrc.h \
        mysource1.h \
        mysource2.h

FORMS += \
        GUI/guisrc.ui

win32: {
    include("c:/dev/opencv/opencv.pri")
}

unix: !macx {
    CONFIG += link_pkgconfig
    PKGCONFIG += opencv
}

unix: macx {
    INCLUDEPATH += "/usr/local/include"
    LIBS += -L"/usr/local/lib" -lopencv_world
}

Test project config file:

CMake

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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
CMAKE_MINIMUM_REQUIRED(VERSION 3.1)

PROJECT(ProjTests
        LANGUAGES CXX)

set(MYPROJECT_DIR "..")

add_definitions(-DGTEST_LANGUAGE_CXX11)
set(CMAKE_CXX_STANDARD 14)

find_package(Threads REQUIRED)
find_package(Qt5 COMPONENTS Core Widgets REQUIRED)
find_package(OpenCV REQUIRED)

if ($ENV{GOOGLETEST_DIR})
    SET(GOOGLETEST_DIR $ENV{GOOGLETEST_DIR})
else ()
    message(WARNING "Using googletest src dir specified at Qt Creator wizard")
    SET(GOOGLETEST_DIR "/home/user/Tools/googletest")
endif ()
if (EXISTS ${GOOGLETEST_DIR})
    SET(GTestSrc ${GOOGLETEST_DIR}/googletest)
    SET(GMockSrc ${GOOGLETEST_DIR}/googlemock)
else ()
    message( FATAL_ERROR "No googletest src dir found - set GOOGLETEST_DIR to enable!")
endif ()


include_directories(${GTestSrc}
                    ${GTestSrc}/include
                    ${GMockSrc}
                    ${GMockSrc}/include
                    ${OpenCV_INCLUDE_DIRS}
                    ${MYPROJECT_DIR}
                    ${MYPROJECT_DIR}/GUI)

add_executable(${PROJECT_NAME} main.cpp
                tst_mysource1.cpp
                tst_mysource2.cpp
               ${MYPROJECT_DIR}/mysource1.cpp
               ${MYPROJECT_DIR}/mysource2.cpp
               ${GTestSrc}/src/gtest-all.cc
               ${GMockSrc}/src/gmock-all.cc)
add_test(${PROJECT_NAME} COMMAND ${PROJECT_NAME})
target_link_libraries(${PROJECT_NAME} PRIVATE Threads::Threads Qt5::Core Qt5::Widgets ${OpenCV_LIBS})

One last thing that is worth mentioning, is that Qt Creator usually likes to compile files into a folder one level above the source folder, which in the current setup means the that the tests will be placed directly into the MyProject folder along with the other sources. This is obviously not desirable, so a small adjustment of the “Build directory” entry in Qt Creator’s “Projects Mode” window will solve such issues.

As with many things, there are advantages and disadvantages to such a setup. An advantage is that despite the fact that the source and test trees are handled separately, continuous and seamless development is still provided, as the same source files are used in both projects. A disadvantage is that the IDE will automatically add source files into one project file only, the other one needs to be maintained manually when new files are created. As mentioned before, this is not that big of a burden if one uses CMake for the test project (I know, I know, with large projects this still can be a problem, but a clever script might quickly convert this to a no-problem).

As always, thanks for reading.