diff --git a/CMakeLists.txt b/CMakeLists.txt
index 96d6befb38178083ec9930d531c11fc9923998d6..5ed494eb3064e211f8176f23b0465f6ea77045d0 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -67,6 +67,7 @@ if(ZERO_TOLERANCE)
     set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Werror -Wfatal-errors")
 endif()
 if(BORNAGAIN_GUI)
+    include(SearchQt)
     set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DQT_MESSAGELOGCONTEXT=ON")
 endif()
 include(BornAgainConfiguration)
@@ -108,7 +109,6 @@ add_subdirectory(Tests/UnitTests/Numeric)
 add_subdirectory(Tests/Performance/Core)
 
 if(BORNAGAIN_GUI)
-    include(SearchQt)
     add_subdirectory(ThirdParty/GUI)
     add_subdirectory(GUI)
     add_subdirectory(Tests/UnitTests/GUI)
diff --git a/Core/Basics/Assert.h b/Core/Basics/Assert.h
index b9a2d457f1d8484fc83186190020c828ada6f557..fd0e15b53c4bf2ece482c2717e6d6e23ee980a6e 100644
--- a/Core/Basics/Assert.h
+++ b/Core/Basics/Assert.h
@@ -15,22 +15,19 @@
 #ifndef BORNAGAIN_CORE_BASICS_ASSERT_H
 #define BORNAGAIN_CORE_BASICS_ASSERT_H
 
-#ifdef BORNAGAIN_GUI
+// ASSERT must be declared as a macro, not a function, in order for the error
+// message to correctly report the source line where the assertion failed.
+
+#ifdef QT_MESSAGELOGCONTEXT
 
 #include <QtGlobal>
-#define ASSERT(condition) \
-    if (!(condition)) \
-        qFatal("assertion failed");
+#define ASSERT(condition) if (!(condition)) qFatal("Assertion failed")
 
-#else // The non-GUI case is used by our test suite
+#else // QT_MESSAGELOGCONTEXT undefined
 
-#include <iostream>
-#define ASSERT(condition) \
-    if (!(condition)) { \
-        std::cerr << "assertion failed" << std::endl; \
-        exit(1); \
-    }
+#include <cassert>
+#define ASSERT(condition) assert(condition)
 
-#endif // BORNAGAIN_GUI
+#endif // QT_MESSAGELOGCONTEXT
 
 #endif // BORNAGAIN_CORE_BASICS_ASSERT_H
diff --git a/Core/CMakeLists.txt b/Core/CMakeLists.txt
index 505ae524815f6f08c9b554b339b7b08524eff5ee..546a2dfae3976c34d95d8ca600cdb0a866ca6a07 100644
--- a/Core/CMakeLists.txt
+++ b/Core/CMakeLists.txt
@@ -154,6 +154,10 @@ target_include_directories(${library_name}
     ${CMAKE_SOURCE_DIR}/ThirdParty/Core/cerf_wrapper # TEMPORARY
     SYSTEM PUBLIC ${EIGEN3_INCLUDE_DIRS}
     )
+if(BORNAGAIN_GUI)
+    target_include_directories(${library_name} PUBLIC ${Qt5Core_INCLUDE_DIRS})
+endif()
+
 target_link_libraries(${library_name} ${Boost_LIBRARIES} ${FFTW3_LIBRARIES} ${GSL_LIBRARIES}
     ${tspectrum_LIBRARY} ${Cerf_LIBRARIES})
 
diff --git a/GUI/main/CMakeLists.txt b/GUI/main/CMakeLists.txt
index 3e2450a18dbb442a395f22a26a119a91cbc82d52..2acb75efd7c7b4b77a81ac0dae6f796e977b385b 100644
--- a/GUI/main/CMakeLists.txt
+++ b/GUI/main/CMakeLists.txt
@@ -3,8 +3,8 @@
 ###############################################################################
 set(executable_name BornAgain)
 
-set(source_files main.cpp appoptions.cpp)
-set(include_files appoptions.h)
+set(source_files main.cpp appoptions.cpp MessageHandler.cpp)
+set(include_files appoptions.h MessageHandler.h )
 
 # -----------------------------------------------------------------------------
 # Qt configuration
diff --git a/GUI/main/MessageHandler.cpp b/GUI/main/MessageHandler.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..db6b72c797c06468911662294c24981f30bcd163
--- /dev/null
+++ b/GUI/main/MessageHandler.cpp
@@ -0,0 +1,62 @@
+// ************************************************************************** //
+//
+//  BornAgain: simulate and fit scattering at grazing incidence
+//
+//! @file      GUI/main/MessageHandler.cpp
+//! @brief     Implements function MessageHandler
+//!
+//! @homepage  http://www.bornagainproject.org
+//! @license   GNU General Public License v3 or higher (see COPYING)
+//! @copyright Forschungszentrum Jülich GmbH 2018
+//! @authors   Scientific Computing Group at MLZ (see CITATION, AUTHORS)
+//
+// ************************************************************************** //
+
+#include "GUI/main/MessageHandler.h"
+#include <iostream>
+#include <QMessageBox>
+
+#ifndef QT_NO_DEBUG
+#define context(ctx) " [" << ctx.function << "]"
+#else
+#define context(ctx) ""
+#endif
+
+//! This is set by main to be the message handler of our GUI.
+void MessageHandler(QtMsgType type, const QMessageLogContext& ctx, const QString& msg)
+{
+    switch (type) {
+        case QtDebugMsg:
+            if (!msg.size()) // KDE will pass a zero-length msg qstring
+                break;
+            std::cerr << "DEBUG: " << msg.toStdString() << std::endl;
+            break;
+        case QtInfoMsg:
+            std::cerr << "INFO: " << msg.toStdString() << context(ctx) << std::endl;
+            break;
+        case QtWarningMsg:
+        default:
+            if (msg.left(4) == "QXcb")
+                return;
+            std::cerr << "WARNING: " << msg.toStdString() <<  " [" << ctx.function << "]"
+                      << std::endl;
+            QMessageBox::warning(QApplication::activeWindow(), qAppName(), msg);
+            break;
+        case QtFatalMsg:
+            std::cerr << "FATAL: " << msg.toStdString() << " [" << ctx.function << "]" << std::endl;
+            qApp->restoreOverrideCursor();
+            QMessageBox::critical(
+                QApplication::activeWindow(), qAppName(),
+                "Sorry, you encountered a fatal bug.\n"
+                "The application will terminate.\n"
+                "Please note the following and inform the maintainers.\n\n"
+                "Error:\n"
+                    + msg
+                    + "\n"
+                      "Context:\n"
+                    + ctx.function + "\n"
+            );
+            qApp->quit();
+            exit(1);
+    }
+}
diff --git a/GUI/main/MessageHandler.h b/GUI/main/MessageHandler.h
new file mode 100644
index 0000000000000000000000000000000000000000..961d4248d016ed759561e554fa5ffe8fa58e840d
--- /dev/null
+++ b/GUI/main/MessageHandler.h
@@ -0,0 +1,17 @@
+// ************************************************************************** //
+//
+//  BornAgain: simulate and fit scattering at grazing incidence
+//
+//! @file      GUI/main/MessageHandler.cpp
+//! @brief     Declares function MessageHandler
+//!
+//! @homepage  http://www.bornagainproject.org
+//! @license   GNU General Public License v3 or higher (see COPYING)
+//! @copyright Forschungszentrum Jülich GmbH 2018
+//! @authors   Scientific Computing Group at MLZ (see CITATION, AUTHORS)
+//
+// ************************************************************************** //
+
+#include <QApplication>
+
+void MessageHandler(QtMsgType type, const QMessageLogContext& ctx, const QString& msg);
diff --git a/GUI/main/main.cpp b/GUI/main/main.cpp
index 9b6d77f59c35ed85e33f5afd4803cd1dcc769b19..e997a45ba594f2ea75bdf402835b5c512ef67fa9 100644
--- a/GUI/main/main.cpp
+++ b/GUI/main/main.cpp
@@ -17,7 +17,7 @@
 #include "GUI/coregui/mainwindow/mainwindow.h"
 #include "GUI/coregui/utils/hostosinfo.h"
 #include "GUI/main/appoptions.h"
-#include <QApplication>
+#include "GUI/main/MessageHandler.h"
 #include <QLocale>
 #include <QMetaType>
 
@@ -37,8 +37,7 @@ int main(int argc, char* argv[])
 
     QApplication app(argc, argv);
 
-    if (!options.find("with-debug"))
-        qInstallMessageHandler(messageHandler);
+    qInstallMessageHandler(MessageHandler);
 
     std::unique_ptr<SplashScreen> splash;
     if (!options.find("no-splash")) {
diff --git a/Tests/UnitTests/Core/Basics/TestAssert.cpp b/Tests/UnitTests/Core/Basics/TestAssert.cpp
index e52a734c12d74501563bae4c015bb98dc5463ece..06ef68b29c7e3dc61591e3aebcc96c5f8399a92b 100644
--- a/Tests/UnitTests/Core/Basics/TestAssert.cpp
+++ b/Tests/UnitTests/Core/Basics/TestAssert.cpp
@@ -8,5 +8,9 @@ class TestAssert : public ::testing::Test
 TEST_F(TestAssert, Assert)
 {
     EXPECT_NO_THROW(ASSERT(1));
-    EXPECT_EXIT(ASSERT(0), ::testing::ExitedWithCode(1), "assertion failed");
+#ifdef QT_MESSAGELOGCONTEXT
+    EXPECT_EXIT(ASSERT(0), ::testing::KilledBySignal(6), "Assertion failed");
+#else
+    EXPECT_EXIT(ASSERT(0), ::testing::KilledBySignal(6), "Assertion .* failed");
+#endif
 }
diff --git a/Tests/UnitTests/Core/CMakeLists.txt b/Tests/UnitTests/Core/CMakeLists.txt
index 294b8fcd3ee424fdf4e604845524abe0dc4306b0..6e312354ee6fc1717a8801e7c715444c76610e98 100644
--- a/Tests/UnitTests/Core/CMakeLists.txt
+++ b/Tests/UnitTests/Core/CMakeLists.txt
@@ -2,11 +2,14 @@ include(GoogleTest) # provides gtest_discover_tests
 
 set(test UnitTestCore)
 
-include_directories(${CMAKE_SOURCE_DIR}/Tests/UnitTests/utilities)
-
 file(GLOB source_files "*/*.cpp" ${CMAKE_SOURCE_DIR}/Tests/GTestWrapper/TestAll.cpp)
 
 add_executable(${test} ${source_files})
+target_include_directories(${test} PUBLIC ${CMAKE_SOURCE_DIR}/Tests/UnitTests/utilities)
 target_link_libraries(${test} ${BornAgainCore_LIBRARY} gtest)
+if(BORNAGAIN_GUI)
+    target_include_directories(${test} PUBLIC ${Qt5Core_INCLUDE_DIRS})
+    target_link_libraries(${test} ${Qt5Core_LIBRARIES})
+endif()
 
 gtest_discover_tests(${test} TEST_PREFIX Core.Unit.)