diff --git a/CMakeLists.txt b/CMakeLists.txt
index ca7666e19e244029e88c2b0c046c741f5be81f8f..405f2b320ac0c8d70910ad3dc0e30d0e1e37cda8 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -3,7 +3,6 @@ cmake_minimum_required(VERSION 3.7)
 project(jami-qt)
 
 # todo: show error if Qt version < 5.14
-# todo: move to common.cmake
 set(CMAKE_CXX_STANDARD 17)
 set(CMAKE_CXX_STANDARD_REQUIRED ON)
 set(CMAKE_CXX_FLAGS_DEBUG "-Og -ggdb")
@@ -13,11 +12,11 @@ set(CMAKE_AUTORCC ON)
 set(CMAKE_AUTOUIC ON)
 set(CMAKE_INCLUDE_CURRENT_DIR ON)
 
-set(SRC_DIR ${CMAKE_CURRENT_SOURCE_DIR}/src)
+set(SRC_DIR ${CMAKE_SOURCE_DIR}/src)
 set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${Qt5Widgets_EXECUTABLE_COMPILE_FLAGS}")
 
-set(QML_RESOURCES ${CMAKE_CURRENT_SOURCE_DIR}/resources.qrc)
-set(QML_RESOURCES_QML ${CMAKE_CURRENT_SOURCE_DIR}/qml.qrc)
+set(QML_RESOURCES ${CMAKE_SOURCE_DIR}/resources.qrc)
+set(QML_RESOURCES_QML ${CMAKE_SOURCE_DIR}/qml.qrc)
 
 # todo: check LRC exists?
 set(LRC ${CMAKE_CURRENT_SOURCE_DIR}/../install/lrc)
@@ -33,90 +32,90 @@ set(QML_LIBS
     Qt5::Core)
 
 set(COMMON_SOURCES
-    src/bannedlistmodel.cpp
-    src/accountlistmodel.cpp
-    src/networkmanager.cpp
-    src/runguard.cpp
-    src/updatemanager.cpp
-    src/webchathelpers.cpp
-    src/main.cpp
-    src/smartlistmodel.cpp
-    src/utils.cpp
-    src/rendermanager.cpp
-    src/connectivitymonitor.cpp
-    src/mainapplication.cpp
-    src/messagesadapter.cpp
-    src/accountadapter.cpp
-    src/calladapter.cpp
-    src/conversationsadapter.cpp
-    src/distantrenderer.cpp
-    src/previewrenderer.cpp
-    src/avadapter.cpp
-    src/contactadapter.cpp
-    src/pluginadapter.cpp
-    src/settingsadapter.cpp
-    src/deviceitemlistmodel.cpp
-    src/pluginitemlistmodel.cpp
-    src/mediahandleritemlistmodel.cpp
-    src/preferenceitemlistmodel.cpp
-    src/mediacodeclistmodel.cpp
-    src/accountstomigratelistmodel.cpp
-    src/audioinputdevicemodel.cpp
-    src/videoinputdevicemodel.cpp
-    src/audiooutputdevicemodel.cpp
-    src/pluginlistpreferencemodel.cpp
-    src/videoformatfpsmodel.cpp
-    src/videoformatresolutionmodel.cpp
-    src/audiomanagerlistmodel.cpp
-    src/qmlregister.cpp
-    src/utilsadapter.cpp)
+    ${SRC_DIR}/bannedlistmodel.cpp
+    ${SRC_DIR}/accountlistmodel.cpp
+    ${SRC_DIR}/networkmanager.cpp
+    ${SRC_DIR}/runguard.cpp
+    ${SRC_DIR}/updatemanager.cpp
+    ${SRC_DIR}/webchathelpers.cpp
+    ${SRC_DIR}/main.cpp
+    ${SRC_DIR}/smartlistmodel.cpp
+    ${SRC_DIR}/utils.cpp
+    ${SRC_DIR}/rendermanager.cpp
+    ${SRC_DIR}/connectivitymonitor.cpp
+    ${SRC_DIR}/mainapplication.cpp
+    ${SRC_DIR}/messagesadapter.cpp
+    ${SRC_DIR}/accountadapter.cpp
+    ${SRC_DIR}/calladapter.cpp
+    ${SRC_DIR}/conversationsadapter.cpp
+    ${SRC_DIR}/distantrenderer.cpp
+    ${SRC_DIR}/previewrenderer.cpp
+    ${SRC_DIR}/avadapter.cpp
+    ${SRC_DIR}/contactadapter.cpp
+    ${SRC_DIR}/pluginadapter.cpp
+    ${SRC_DIR}/settingsadapter.cpp
+    ${SRC_DIR}/deviceitemlistmodel.cpp
+    ${SRC_DIR}/pluginitemlistmodel.cpp
+    ${SRC_DIR}/mediahandleritemlistmodel.cpp
+    ${SRC_DIR}/preferenceitemlistmodel.cpp
+    ${SRC_DIR}/mediacodeclistmodel.cpp
+    ${SRC_DIR}/accountstomigratelistmodel.cpp
+    ${SRC_DIR}/audioinputdevicemodel.cpp
+    ${SRC_DIR}/videoinputdevicemodel.cpp
+    ${SRC_DIR}/audiooutputdevicemodel.cpp
+    ${SRC_DIR}/pluginlistpreferencemodel.cpp
+    ${SRC_DIR}/videoformatfpsmodel.cpp
+    ${SRC_DIR}/videoformatresolutionmodel.cpp
+    ${SRC_DIR}/audiomanagerlistmodel.cpp
+    ${SRC_DIR}/qmlregister.cpp
+    ${SRC_DIR}/utilsadapter.cpp)
 
 set(COMMON_HEADERS
-    src/networkmanager.h
-    src/smartlistmodel.h
-    src/updatemanager.h
-    src/utils.h
-    src/bannedlistmodel.h
-    src/version.h
-    src/accountlistmodel.h
-    src/runguard.h
-    src/lrcinstance.h
-    src/globalsystemtray.h
-    src/appsettingsmanager.h
-    src/webchathelpers.h
-    src/rendermanager.h
-    src/connectivitymonitor.h
-    src/jamiavatartheme.h
-    src/mainapplication.h
-    src/qrimageprovider.h
-    src/messagesadapter.h
-    src/accountadapter.h
-    src/tintedbuttonimageprovider.h
-    src/calladapter.h
-    src/conversationsadapter.h
-    src/distantrenderer.h
-    src/previewrenderer.h
-    src/qmladapterbase.h
-    src/avadapter.h
-    src/contactadapter.h
-    src/pluginadapter.h
-    src/settingsadapter.h
-    src/deviceitemlistmodel.h
-    src/pluginitemlistmodel.h
-    src/mediahandleritemlistmodel.h
-    src/preferenceitemlistmodel.h
-    src/mediacodeclistmodel.h
-    src/accountstomigratelistmodel.h
-    src/audioinputdevicemodel.h
-    src/videoinputdevicemodel.h
-    src/audiooutputdevicemodel.h
-    src/pluginlistpreferencemodel.h
-    src/videoformatfpsmodel.h
-    src/videoformatresolutionmodel.h
-    src/audiomanagerlistmodel.h
-    src/qmlregister.h
-    src/qtutils.h
-    src/utilsadapter.h)
+    ${SRC_DIR}/networkmanager.h
+    ${SRC_DIR}/smartlistmodel.h
+    ${SRC_DIR}/updatemanager.h
+    ${SRC_DIR}/utils.h
+    ${SRC_DIR}/bannedlistmodel.h
+    ${SRC_DIR}/version.h
+    ${SRC_DIR}/accountlistmodel.h
+    ${SRC_DIR}/runguard.h
+    ${SRC_DIR}/lrcinstance.h
+    ${SRC_DIR}/globalsystemtray.h
+    ${SRC_DIR}/appsettingsmanager.h
+    ${SRC_DIR}/webchathelpers.h
+    ${SRC_DIR}/rendermanager.h
+    ${SRC_DIR}/connectivitymonitor.h
+    ${SRC_DIR}/jamiavatartheme.h
+    ${SRC_DIR}/mainapplication.h
+    ${SRC_DIR}/qrimageprovider.h
+    ${SRC_DIR}/messagesadapter.h
+    ${SRC_DIR}/accountadapter.h
+    ${SRC_DIR}/tintedbuttonimageprovider.h
+    ${SRC_DIR}/calladapter.h
+    ${SRC_DIR}/conversationsadapter.h
+    ${SRC_DIR}/distantrenderer.h
+    ${SRC_DIR}/previewrenderer.h
+    ${SRC_DIR}/qmladapterbase.h
+    ${SRC_DIR}/avadapter.h
+    ${SRC_DIR}/contactadapter.h
+    ${SRC_DIR}/pluginadapter.h
+    ${SRC_DIR}/settingsadapter.h
+    ${SRC_DIR}/deviceitemlistmodel.h
+    ${SRC_DIR}/pluginitemlistmodel.h
+    ${SRC_DIR}/mediahandleritemlistmodel.h
+    ${SRC_DIR}/preferenceitemlistmodel.h
+    ${SRC_DIR}/mediacodeclistmodel.h
+    ${SRC_DIR}/accountstomigratelistmodel.h
+    ${SRC_DIR}/audioinputdevicemodel.h
+    ${SRC_DIR}/videoinputdevicemodel.h
+    ${SRC_DIR}/audiooutputdevicemodel.h
+    ${SRC_DIR}/pluginlistpreferencemodel.h
+    ${SRC_DIR}/videoformatfpsmodel.h
+    ${SRC_DIR}/videoformatresolutionmodel.h
+    ${SRC_DIR}/audiomanagerlistmodel.h
+    ${SRC_DIR}/qmlregister.h
+    ${SRC_DIR}/qtutils.h
+    ${SRC_DIR}/utilsadapter.h)
 
 find_package(Qt5 CONFIG REQUIRED
     Core
diff --git a/docker/Dockerfile_debian_11 b/docker/Dockerfile_debian_11
index ca80ee8f73c2393cf0b8ae8afad15c19c12fc6e6..759d4a0153393c3f411acb5229b4076740c4d678 100644
--- a/docker/Dockerfile_debian_11
+++ b/docker/Dockerfile_debian_11
@@ -1,5 +1,9 @@
 FROM debian:bullseye-20200908
 
+ENV DEBIAN_FRONTEND noninteractive
+ENV QT_QUICK_BACKEND software
+ENV QT_QPA_PLATFORM offscreen
+
 RUN apt-get clean
 RUN apt-get update
 RUN apt-get install -y -o Acquire::Retires=10 \
@@ -57,12 +61,24 @@ RUN apt-get install -y -o Acquire::Retires=10 \
         libvdpau-dev \
         libssl-dev
 RUN apt-get install -y qtmultimedia5-dev \
-        libqt5svg5* \
+        libqt5svg5-dev \
         qtwebengine5-dev \
         qtdeclarative5-dev \
         qtquickcontrols2-5-dev \
-        qml-module-qtquick* \
+        libqt5svg5-dev \
+        qml-module-qtquick2 \
+        qml-module-qtquick-controls \
+        qml-module-qtquick-controls2 \
+        qml-module-qtquick-dialogs \
+        qml-module-qtquick-layouts \
+        qml-module-qtquick-privatewidgets \
+        qml-module-qtquick-shapes \
+        qml-module-qtquick-window2 \
+        qml-module-qtquick-templates2
         qml-module-qt-labs-platform \
         qml-module-qtwebengine \
         qml-module-qtwebchannel \
-        pandoc
\ No newline at end of file
+        qml-module-qttest \
+        pandoc \
+        googletest \
+        libgtest-dev
\ No newline at end of file
diff --git a/src/main.cpp b/src/main.cpp
index a586ab1b526e161b40b36d1b5eb190aa4ea1c28d..fe63dbf10a34dee7efbe1b820d9029023eb66b99 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -27,6 +27,8 @@
 
 #include <clocale>
 
+#ifndef ENABLE_TESTS
+
 static char**
 parseInputArgument(int& argc, char* argv[], char* argToParse)
 {
@@ -88,3 +90,4 @@ main(int argc, char* argv[])
     guard.release();
     return ret;
 }
+#endif
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..59dcd7e56e0a7fdca19714286a2fc65586462138
--- /dev/null
+++ b/tests/CMakeLists.txt
@@ -0,0 +1,65 @@
+find_package(Qt5 CONFIG REQUIRED QuickTest Test)
+find_package(GTest REQUIRED)
+
+enable_testing(true)
+set(QMLTEST_LIBS ${QML_LIBS} Qt5::QuickTest)
+
+set(TESTS_INCLUDES
+    ${CMAKE_SOURCE_DIR}/src
+    ${CMAKE_SOURCE_DIR}/tests/qml)
+
+include_directories(${CMAKE_CURRENT_SOURCE_DIR}
+    ${CMAKE_SOURCE_DIR}/src
+    ${LRC}/include/libringclient
+    ${LRC}/include)
+
+# Common jami files
+add_library(test_common_obj OBJECT ${COMMON_SOURCES} ${COMMON_HEADERS})
+target_link_libraries(test_common_obj ${QMLTEST_LIBS})
+target_compile_definitions(test_common_obj PRIVATE ENABLE_TESTS="ON")
+
+# QML tests
+add_executable(qml_tests
+    ${CMAKE_SOURCE_DIR}/tests/qml/main.cpp
+    ${QML_RESOURCES}
+    ${QML_RESOURCES_QML}
+    $<TARGET_OBJECTS:test_common_obj>)
+
+target_link_libraries(qml_tests
+    ${QMLTEST_LIBS}
+    ${test_common_objects}
+    ${ringclient}
+    ${qrencode})
+
+target_include_directories(qml_tests PUBLIC
+    ${TESTS_INCLUDES}
+    ${LRC}/include/libringclient
+    ${LRC}/include)
+
+target_compile_definitions(qml_tests PRIVATE ENABLE_TESTS="ON")
+
+add_test(NAME QmlTests COMMAND qml_tests -input ${PROJECT_SOURCE_DIR}/tests/qml/)
+
+# Unittests
+add_executable(unittests
+    ${CMAKE_SOURCE_DIR}/tests/unittests/main_unittests.cpp
+    ${QML_RESOURCES}
+    ${QML_RESOURCES_QML}
+    $<TARGET_OBJECTS:test_common_obj>)
+
+target_link_libraries(unittests
+    ${QMLTEST_LIBS}
+    ${test_common_objects}
+    ${ringclient}
+    ${qrencode}
+    gtest
+    pthread)
+
+target_include_directories(unittests PUBLIC
+    ${TESTS_INCLUDES}
+    ${LRC}/include/libringclient
+    ${LRC}/include)
+
+target_compile_definitions(unittests PRIVATE ENABLE_TESTS="ON")
+
+add_test(NAME UnitTests COMMAND unittests)
diff --git a/tests/qml/main.cpp b/tests/qml/main.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..bc90fc6e7dbae1c883c2b67d1941c67d03d3b95f
--- /dev/null
+++ b/tests/qml/main.cpp
@@ -0,0 +1,21 @@
+#include <QtQuickTest/quicktest.h>
+#include <QQmlEngine>
+#include <QQmlContext>
+
+class Setup : public QObject
+{
+    Q_OBJECT
+
+public:
+    Setup() {}
+
+public slots:
+
+    void qmlEngineAvailable(QQmlEngine *engine)
+    {
+        engine->addImportPath("qrc:/tests/qml");
+    }
+};
+
+QUICK_TEST_MAIN_WITH_SETUP(testqml, Setup)
+#include "main.moc"
diff --git a/tests/qml/tst_test.qml b/tests/qml/tst_test.qml
new file mode 100644
index 0000000000000000000000000000000000000000..26e4fa9bce61bdc92c50525b2dbf8335c6a080c5
--- /dev/null
+++ b/tests/qml/tst_test.qml
@@ -0,0 +1,10 @@
+import QtQuick 2.14
+import QtTest 1.2
+
+TestCase {
+    name: "DummyTests"
+
+    function test_dummy() {
+        verify(true, "dummy test failed!")
+    }
+}
diff --git a/tests/unittests/dummy_unittest.cpp b/tests/unittests/dummy_unittest.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..147a0e79b1d594871375570f2c092919046ebf54
--- /dev/null
+++ b/tests/unittests/dummy_unittest.cpp
@@ -0,0 +1,7 @@
+#include <gtest/gtest.h>
+
+TEST(DummyTest, TestDummy)
+{
+    EXPECT_EQ(1, 1);
+    ASSERT_EQ(0, 0);
+}
diff --git a/tests/unittests/main_unittests.cpp b/tests/unittests/main_unittests.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..e2615987df65ed96a9f2f877c3918acdf299ad61
--- /dev/null
+++ b/tests/unittests/main_unittests.cpp
@@ -0,0 +1,10 @@
+#include <QApplication>
+#include <gtest/gtest.h>
+
+int main(int argc, char *argv[])
+{
+    QApplication a(argc, argv);
+    a.processEvents();
+    ::testing::InitGoogleTest(&argc, argv);
+    return RUN_ALL_TESTS();
+}