How to structure project while unit-testing Qt app by QTestLib - qt

I got my Qt project and I'm using Qt Creator. I want to unit-test all my code.
However I'm quite new at QTestLib framework but everyone recommended it for testing Qt-based source. Now I'm a little confused how to structure test project with app project.
Can I put all source and testing code in same project? If so, how could I manage them? I didn't find any option that let me start app or start test in one project.
If I put app source and testing code in separate projects, the testing project will reference app project, that's not quite convenient.
For lots for classes required to be tested, how do I manage testing code?
How do you guys manage testing code in such a situation? Thanks.

First structure source like below:
MyApp
MyAppUnitTest
Under MyApp project, use a MyAppSrc.pri to locate source files:
SOURCES += \
../../../framework/src/myapp.cpp \
../../../framework/src/mycontrol.cpp
HEADERS += \
../../../framework/inc/myapp.h \
../../../framework/inc/mycontrol.h
INCLUDEPATH += ../../../framework/extlibs
Include this .pri in MyApp.pro like:
include(MyAppSrc.pri)
Then structure the testing project exactly like the main project, with one extra include in MyAppUnitTest.pro:
include(MyAppUnitTestSrc.pri)
include(../MyApp/MyAppSrc.pri)

I use this approach: http://xilexio.org/?p=125
Namely, place a test config in the single .pro file that builds everything. File hierarchy:
myproject.pro
src/
Example1.cpp
Example2.cpp
Example1.h
Example2.h
test/
ExampleTest.cpp
ExampleTest.h
myproject.pro file:
QT += #needed modules
CONFIG += qt c++11
HEADERS += \
src/Example1.h \
src/Example2.h
SOURCES += \
src/Example1.h \
src/Example2.h
test{
message(Configuring test build...)
TEMPLATE = app
TARGET = myapptests
QT += testlib
HEADERS += \
test/ExampleTest.h
SOURCES += \
test/ExampleTest.cpp
}
else{
TEMPLATE = lib
TARGET = myapp
CONFIG += plugin
TARGET = $$qtLibraryTarget($$TARGET)
}
In my example, I'm building a plugin library, but the method should work for an app as well. In the case of an app, it is likely that SOURCES -= src/main.cpp is needed under the else clause, plugin libraries don't have it. If this is not done, the main() of the app will clash with the main() of the unit tests.
ExampleTest.cpp looks like the following:
#include "ExampleTest.h"
void ExampleTest::exampleTest(){
//Do the tests
}
QTEST_MAIN(ExampleTest)
ExampleTest.h looks like the following:
#include <QtTest/QtTest>
class ExampleTest : public QObject {
Q_OBJECT
private slots:
void exampleTest();
};
To build the project tests, in a separate directory than the regular build, run:
qmake path/to/myproject.pro "CONFIG += test"

I like the other answers but I would like to also give some feedback how we do this at the company I currently work for:
Create a subdirs project (this will be the top-level project that will manage ALL including your library project or whatever you want to test)
+-----MyProject (top-level subdirs)
Add your library projects as a sub-project
+-----MyProject (top-level subdirs)
|
+-----Library (library project, UI project etc.)
Add another subdirs projects (for the tests)
+-----MyProject (top-level subdirs)
|
+-----Library (library project, UI project etc.)
|
+-----Tests (subdirs for tests)
Create a QUnitTest project an add it to the testing subdirs project
+-----MyProject (subdirs)
|
+-----Library (library project, UI project etc.)
|
+-----Tests (subdirs for tests)
|
+----- TestA (QUnitTest project for testing feature A)
Add as many tests as you see fit
...
|
+-----Tests (subdirs for test)
|
+----- TestA (QUnitTest project for testing feature A)
|
+----- TestB (QUnitTest project for testing feature B)
|
+----- TestC (QUnitTest project for testing feature C)
|
...
|
+----- TestZ (QUnitTest project for testing feature Z)
If you need to group test in groups you can also use subdirs to do that. subdirs also ensures creating of real directories in your file system. If you want to avoid too much subdirsing you can group the tests in folders that you have created on your own in your filesystem inside the Tests project folder.
Beside that I would also recommend to add a subdirs for template projects.
+-----MyProject (subdirs)
|
+-----Library (library project, UI project etc.)
|
+-----Tests (subdirs for tests)
| |
| ...
|
+-----Templates (subdirs for template projects
|
+----- TemplateA (template project for feature A)
|
+----- TemplateB (template project for feature B)
|
+----- TemplateAB (template project for feature A and B together)
|
...
|
+----- TemplateZ (template project for feature Z)
This is of course based on your library's functionality. With template projects I mean custom widgets etc. that link against your library and expose selectively (or all) of its functionality in the way it's supposed to appear to the user. For example if you have a library that manages various camera devices you can create a template project for each camera device thus allowing for the users of your library to just copy-paste the specific template project and expand it or at least see how the integration of your library is supposed to happen in general. This allows reducing the documentation and at the same time giving nice self-contained examples that should reduce the development time that is otherwise spent in figuring out how the integration and usage of the library works (you can say it's sort of a set of Hello World projects :)). Last but not least you can outline solutions for different use-cases.

I use Qt Creator by CMake instead of qmake to build my Qt project.
Basically I have two folders:
src
tests
Each test is a program in itself testing a class. The app to be tested is compiled as a library.. You compile all your sources in the folder src as a library.
// ClassTest.cpp
#include "ClassTest.h"
#include "Class2Test.h" // Class of the app
#include <QtTest/QtTest>
ClassTest::ClassTest( QObject* parent )
: QObject(parent)
{
}
QTEST_MAIN( ClassTest )
#include "ClassTest.moc"
You just have to link your lib to your test executable.
Example:
in the src folder CMakeLists.txt example
add_library( MyAPP
SHARED
Class2Test.cpp
)
target_link_libraries( MyAPP
${QT_LIBRARIES}
)
in the tests folder CMakeLists.txt example, for each test.
qt4_automoc( ${test_src} )
add_executable( ${test_name} ${test_src} )
target_link_libraries( ${test_name}
MyAPP
${QT_LIBRARIES}
${QT_QTTEST_LIBRARY}
)
It is still in the same project but you can add a flag to let the user compile the test or not. It is clean because the app stays untouched and it allows you to test each class of your app.

Related

How do you build depended-upon libs before the main project with CMake in Qt?

I've looked at similar questions but haven't found one for this basic scenario. I'm relatively new to CMake. I have a CMake-based Qt 5 project. It's a simple test application; during its build I want to build and statically link the open-source Paho MQTT C lib and the C++ wrapper for it. Those are two separate projects with their own CMakeLists.txt files.
Per Qt's default, it builds into a directory outside the source tree.
I've copied the source trees for these open-source libs under the parent directory of my project, and edited the top-level CMakeLists.txt file to add_subdirectory them.
I also added target_link_libraries. I can get the C lib by itself to build and link into the parent project, but if I add the C++ wrapper, the processing for the C++ wrapper complains that it can't find the C lib... which is true because it hasn't been built yet. A similar complaint for the C lib was solved by simply using Qt's "build all projects" menu item, but that doesn't work when the C++ wrapper lib is added. The wrapper's CMakeLists.txt files issues this:
CMake Error at paho.mqtt.cpp/src/CMakeLists.txt:150 (message):
Could not find Paho MQTT C library
And sure enough it doesn't exist because it has not been built when this preprocessing is done.
UPDATED: Here's my top-level CMakeLists.txt file, revised per Corristo's suggestion, which was successful in getting CMake to parse the entire hierarchy. The project now builds. I'm perplexed that the last two lines here result in an empty string, though. So does a similar attempt for the link directories.
find_package(QT NAMES Qt6 Qt5 COMPONENTS Widgets REQUIRED)
find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Widgets REQUIRED)
set(PROJECT_SOURCES
main.cpp
mainwindow.cpp
mainwindow.h
mainwindow.ui
task.h
task.cpp
task.ui
)
if(${QT_VERSION_MAJOR} GREATER_EQUAL 6)
qt_add_executable(MQTTTest
${PROJECT_SOURCES}
)
else()
if(ANDROID)
add_library(MQTTTest SHARED
${PROJECT_SOURCES}
)
else()
add_executable(MQTTTest
${PROJECT_SOURCES}
)
endif()
endif()
add_subdirectory(paho.mqtt.c)
set(PAHO_MQTT_C_LIB paho-mqtt3a)
set(PAHO_MQTT_C_PATH "${CMAKE_CURRENT_LIST_DIR}/paho.mqtt.c")
add_subdirectory(paho.mqtt.cpp)
target_link_libraries(MQTTTest PRIVATE Qt${QT_VERSION_MAJOR}::Widgets)
target_link_directories(MQTTTest PUBLIC
"${CMAKE_CURRENT_LIST_DIR}/build/paho.mqtt.c/src"
"${CMAKE_CURRENT_LIST_DIR}/build/paho.mqtt.cpp/src")
target_link_libraries(MQTTTest PUBLIC paho-mqtt3a)
target_link_libraries(MQTTTest PUBLIC paho-mqttpp3)
target_include_directories(MQTTTest PUBLIC
"${PROJECT_BINARY_DIR}"
"${CMAKE_CURRENT_LIST_DIR}/paho.mqtt.c/src"
"${CMAKE_CURRENT_LIST_DIR}/paho.mqtt.cpp/src")
get_property(inc_dirs DIRECTORY PROPERTY INCLUDE_DIRECTORIES)
message("Top-level include dirs = ${inc_dirs}")
It is a bit of a hack, but you can use the fact that the CMake find_* commands don't perform a search if the result variable is already set.
From the paho.mqtt.cpp/src/CMakeLists.txt file we find that the output variable for find_library is called PAHO_MQTT_C_LIB and the include directory is expected to be in PAHO_MQTT_C_INC_DIR, which in the original CMakeLists.txt from version 1.0.0 (which seems to be the version you're using) is itself computed from PAHO_MQTT_C_PATH.
Setting these two variables between the two add_subdirectory calls should then make this work:
add_subdirectory(paho.mqtt.c)
set(PAHO_MQTT_C_LIB paho-mqtt3a)
set(PAHO_MQTT_C_PATH "${CMAKE_CURRENT_LIST_DIR}/paho.mqtt.c")
add_subdirectory(paho.mqtt.cpp)
This makes use of the fact that target_link_libraries can be called both with library files (which is what the original paho.mqtt.cpp project expected) and with existing CMake targets (which is what we replaced it with). Linking to a CMake target also automatically introduces a build-order dependency, so this simultaneously ensures that the c library is built before the cpp library.
However, since this relies on the names of the variables used in the paho.mqtt.cpp project as well as the target name of the library target in the paho.mqtt.c project this can break any time you update one of these libraries to a newer version.

qmake: how to build dependencies without TEMPLATE=subdirs

TL;DR: Is there a way to build a target from a .pro file in a different project without using TEMPLATE = subdirs?
Long version:
I have a complex project (mycomplexproject) with lots of subirs and *.pro files. One module (moduleA) depends on a DLL built in a different project outside of this project (anotherproject). Because reasons I cannot create a *.pro file with a subdirs template in somerootdir.
Is there a way to add a dependency in moduleA.pro so anotherproject is built whenever moduleA is built and anotherproject.dll does not exist without having to create a *.pro file in somerootdir?
somerootdir/
anotherproject/ // must be independent of mycomplexproject
anotherproject.pro
lib/
anotherproject.dll // the result of building anotherproject
[...]
mycomplexproject/
core/
core.pro
modules/
moduleA/
moduleA.pro // depends on anotherproject.dll built from anotherproject.pro
moduleB/
moduleB.pro
modules.pro // uses subdir template
mycomplexproject.pro // uses subdir template
P.S.: moduleA is only built under certain circumstances, and building anotherproject is only necessary if moduleAis built.

Qt subprojects and TDD

I am building a Qt application which consists of several components (including plugins). Each sub project contains several classes.
I want to use the "normal" TDD workflow in my development process - namely;
I write a new function/method of a class in a module as a test
Compile the test (which should fail) for that module
Modify the source to correct the error (by adding the func/method etc)
Write test cases for the new function/method
Modify the src code to fix tests that fail
Each sub project will contain a src/ and test/ folders which will hold the source files and unittests respectively
This is what the project directory structure looks like:
myapp
|
|-myapp.pro
|
|--module1/
| |-src/
| |-test/
|
|--module2/
| |-src/
| |-test/
|
.
.
|--moduleN/
| |-src/
| |-test/
How can I setup QCreator so that I can build a subproject or its unittest from the QCreator GUI?
You can make a subdirs project and add the subprojects to its .pro file :
TEMPLATE = subdirs
CONFIG += ordered
SUBDIRS += \
module1 \
module2 \
...
moduleN \
myapp
You should bring the subprojects that others depend on, first in the list. Also notice that the name of the .pro file of the subproject should be the same as it's folder name. This way the subprojects are detected and listed in the Projects pane.
The subprojects module1, module2 ... moduleN could be libraries which have TEMPLATE = lib in their .pro file and myapp should be app containing TEMPLATE = app in the .pro file.
You can use the libraries in each subproject by linking it to the subproject. This can be done by right clicking on the subproject and choosing Add Library and then Internal Library. When you select one library from the list of subprojects, the linking configurations are added to the .pro automatically. It will be like :
win32:CONFIG(release, debug|release): LIBS += -L$$OUT_PWD/../module1/release/ -lmodule1
else:win32:CONFIG(debug, debug|release): LIBS += -L$$OUT_PWD/../module1/debug/ -lmodule1
else:unix: LIBS += -L$$OUT_PWD/../Base/ -lmodule1
INCLUDEPATH += $$PWD/../module1
DEPENDPATH += $$PWD/../module1
You should use subdirs template in .pro file.
I believe, you can start from following link (at the bottom of linked page).

What is the proper way of deploying C++ QML plugins on mobile device?

I've been playing a lot with the Box2D QML plugin and the things are looking really good.
However, I wanted to deploy my sample app on Android (SGS2), but I can't seem to get it work. Whether I try to run it on AVD or on the device, it doesn't work. androiddeployqt finishes successfully, but then I get "Unable to start 'MyApp'" and no other info as to why it failed to start. I can successfully run qml apps on the AVD and the device, but this has something to do with the plugin and I can't find any reference in order to solve it.
I tried setting up DEPLOYMENTFOLDERS in different ways, but if i get them wrong, then the whole thing fails. Even when I don't get an error, in which case I assume I got it right, it still doesn't start.
I've been struggling with this for quiet some time, and can't find any bit of useful information in order to resolve it.
If you know of any project that uses a c++ plugin and can be successfully deployed on android device, that would be good as well.
I am using Qt 5.2.0 compiled for android and the qt5 branch of box2d
We were getting "module not found" errors while we were trying to get our QML module working inside an Android Qt application. With Qt 5.3, we managed to get our QML plugin recognized only by deploying the plugin to the QT_INSTALL_QML directory where official Qt QML modules reside. This directory is /opt/Qt/5.3/android_armv7/qml in our case.
Plugin Side
Our .pro file for the plugin looks like:
TEMPLATE = lib
TARGET = prova
QT += qml quick multimedia
CONFIG += qt plugin c++11 console
CONFIG -= android_install
TARGET = $$qtLibraryTarget($$TARGET)
uri = com.mycompany.qmlcomponents
# Input
SOURCES += \
src1.cpp \
src2.cpp
HEADERS += \
src1.h \
src2.h
##The below is generated automatically by Qt Creator when you create a new "Qt Quick 2 Extension Plugin" project for Android
#Copies the qmldir file to the build directory
!equals(_PRO_FILE_PWD_, $$OUT_PWD) {
copy_qmldir.target = $$OUT_PWD/qmldir
copy_qmldir.depends = $$_PRO_FILE_PWD_/qmldir
copy_qmldir.commands = $(COPY_FILE) \"$$replace(copy_qmldir.depends, /, $$QMAKE_DIR_SEP)\" \"$$replace(copy_qmldir.target, /, $$QMAKE_DIR_SEP)\"
QMAKE_EXTRA_TARGETS += copy_qmldir
PRE_TARGETDEPS += $$copy_qmldir.target
}
#Copies the qmldir file and the built plugin .so to the QT_INSTALL_QML directory
qmldir.files = qmldir
unix {
installPath = $$[QT_INSTALL_QML]/$$replace(uri, \\., /)
qmldir.path = $$installPath
target.path = $$installPath
INSTALLS += target qmldir
}
Our qmldir (in the plugin source tree root) file is:
module com.mycompany.qmlcomponents
plugin prova
Application Side
The .pro file looks like:
TEMPLATE = app
QT += qml quick widgets multimedia
CONFIG+= console
SOURCES += main.cpp
RESOURCES += qml.qrc
# Additional import path used to resolve QML modules in Qt Creator's code model
QML_IMPORT_PATH =
# Default rules for deployment.
include(deployment.pri)
contains(ANDROID_TARGET_ARCH,armeabi-v7a) {
ANDROID_EXTRA_LIBS = \
/opt/Qt/5.3/android_armv7/qml/com/mycompany/qmlcomponents/libprova.so
}
Important Note: Any library that your qml plugin uses must also be listed in ANDROID_EXTRA_LIBS in order to be bundled into the apk. This includes Qt components as well, listing them in QT+= is not enough if you don't use them in your application.
We don't actually know if the inclusion of the extra libprova.so is necessary. It's most probably not.
The main.cpp looks like:
#include <QApplication>
#include <QQmlApplicationEngine>
int main(int argc, char *argv[]){
QApplication app(argc, argv);
QQmlApplicationEngine engine;
engine.load(QUrl(QStringLiteral("qrc:///main.qml")));
return app.exec();
}
The main.qml just includes the plugin like:
import QtQuick 2.2
import QtQuick.Controls 1.1
import QtMultimedia 5.0
import com.mycompany.qmlcomponents 1.0
...
Building and Deployment
The way we build and deploy the plugin is to qmake (from the android-armv7 toolchain of Qt), then make install. It installs the qmldir file and the plugin .so to the QT_INSTALL_QML directory.
The way we build and deploy the project that uses the plugin is to qmake (again, from the android-armv7 toolchain of Qt), then make install INSTALL_ROOT=. (installs to build directory), then run androiddeployqt. The last command creates the Android project structure with the qmldirs in assets/ and libraries in libs/ and bundles the whole thing in an apk via ant. For the details of this procedure, refer to http://qt-project.org/wiki/Android.
In short, we were only able to get our QML plugin recognized inside an Android project by putting it inside the private Qt qml directory. I hope this helps in some way.

QtCreator subdirs project

I'm going to achieve the following hierarchy in my QtCreator project based on subdirs template:
Project:
subproject1 - static library
subproject2 - static library
subproject3 - unittests executable
The only one executable will be in subproject3, which will link against subproject1 and subproject 2.
However build works fine I've noticed some usability issue. When I run the whole project it tries to find executables in my libraries - and fails. I have to run subproject3 instead of the main one.
Is it possible to avoid such situation?
I am using a similar hierarchy for the unit-tests - and the .pro file should have the following lines in it
INCLUDEPATH += ../subproject1/ \
../subproject2/
After adding the folders in the INCLUDEPATH variable, you can add the sources and header that you need using the SOURCES and HEADERS variables.
Hope it helps you

Resources