diff --git a/doc/spec_exceptionHandling.dox b/doc/spec_exceptionHandling.dox
index e6fe5889b92d9ef01683760b19338ebc83b10ee4..093cf355570f3c991d02c503e9be96e01300d773 100644
--- a/doc/spec_exceptionHandling.dox
+++ b/doc/spec_exceptionHandling.dox
@@ -40,7 +40,7 @@ namespace ChimeraTK {
 
 \subsection spec_exceptionHandling_behaviour_runtime_errors Runtime error handling
 - \anchor b_2 2. When a ChimeraTK::runtime_error has been received by the framework (thrown by a device register accessor):
-  - 2.1 The exception status is published as a process variable together with an error message.
+  - \anchor exceptionHandling_b_2_1 2.1 The exception status is published as a process variable together with an error message. [\ref testExceptionHandling_b_2_1 "T"]
     - 2.1.1 The variable \c Devices/\<alias\>/status contains a boolean flag whether the device is in an error state.
     - 2.1.2 The variable \c Devices/\<alias\>/message contains an error message, if the device is in an error state, or an empty string otherwise.
   - \anchor b_2_2 2.2 Read operations will propagate the DataValidity::faulty flag to the owning module / fan out (without changing the actual data value of the process variable):
diff --git a/tests/executables_src/testExceptionHandling.cc b/tests/executables_src/testExceptionHandling.cc
index 6eb4c20bec1a6a8fabce2b3457155590a028f83f..68bf7fc61882b6cfa95d6eca21b060fef32f08e6 100644
--- a/tests/executables_src/testExceptionHandling.cc
+++ b/tests/executables_src/testExceptionHandling.cc
@@ -1,3 +1,4 @@
+#include <cstring>
 #include <future>
 
 #define BOOST_TEST_MODULE testExceptionHandling
@@ -21,6 +22,146 @@
 using namespace boost::unit_test_framework;
 namespace ctk = ChimeraTK;
 
+// TODO: extend/rename test application as required/ make new test applications as the exception
+// handling cases are covered.
+/**************************/
+struct Module : ctk::ApplicationModule {
+  using ctk::ApplicationModule::ApplicationModule;
+
+  ctk::ScalarPollInput<int> fromDevice{this, "REG1", "", "", {"DEVICE"}};
+  ctk::ScalarOutput<int> toCs{this, "o1", "", "", {"CS"}};
+
+  void mainLoop() override {
+    while(true) {
+      readAll();
+      toCs = static_cast<int>(fromDevice);
+      writeAll();
+      boost::this_thread::sleep_for(boost::chrono::milliseconds(100));
+    }
+  }
+};
+
+struct DummyApplication : ctk::Application {
+  //   +----------+  poll_type_variable  +------+
+  //   |Module x<------------------------+Device|
+  //   +----------+                      +------+
+
+  constexpr static char const* ExceptionDummyCDD1 = "(ExceptionDummy:1?map=test.map)";
+  DummyApplication() : Application("testFault") {}
+  ~DummyApplication() { shutdown(); }
+
+  Module m{this, "", ""};
+
+  ctk::ControlSystemModule cs;
+  ctk::DeviceModule device{this, ExceptionDummyCDD1};
+
+  void defineConnections() override {
+    findTag("CS").connectTo(cs);
+    findTag("DEVICE").connectTo(device);
+  }
+};
+
+struct Fixture_noTestFacility {
+  Fixture_noTestFacility()
+  : deviceBackend(boost::dynamic_pointer_cast<ctk::ExceptionDummy>(
+        ChimeraTK::BackendFactory::getInstance().createBackend(DummyApplication::ExceptionDummyCDD1))) {
+
+    deviceBackend->open();
+    testFacitiy.runApplication();
+
+    //
+    // As a test precondition, the DeviceModule  must have completed its startup procedure. The code
+    // block below is a workaround to ensure this.
+    //
+    // Code below depends on behavior of process variable 'status', where 'status' is initialized
+    // with 0 as default after which its written with:
+    //  - 1 (before DeviceModule begins opening the device),
+    //  - 0 (once DeviceModule successfully opens the device/device startup is complete).
+    // This sequence is similar for the associated 'message' variable; replace 0/1 values above
+    // with an empty/non-empty string.
+    //
+    // Thus checking for two writes on these variables is a resonable indication that device startup
+    // is done. (provided the assumption explained above holds).
+    /************************************************************************************************/
+    auto status =
+        testFacitiy.getScalar<int>(ctk::RegisterPath("/Devices") / DummyApplication::ExceptionDummyCDD1 / "status");
+    auto message = testFacitiy.getScalar<std::string>(
+        ctk::RegisterPath("/Devices") / DummyApplication::ExceptionDummyCDD1 / "message");
+
+    CHECK_TIMEOUT(status.readNonBlocking() == true, 100000);
+    CHECK_TIMEOUT(status.readNonBlocking() == true, 100000);
+
+    CHECK_TIMEOUT(message.readNonBlocking() == true, 100000);
+    CHECK_TIMEOUT(message.readNonBlocking() == true, 100000);
+    /************************************************************************************************/
+  }
+  ~Fixture_noTestFacility() { deviceBackend->throwExceptionRead = false; }
+
+  ctk::TestFacility testFacitiy{false};
+  boost::shared_ptr<ctk::ExceptionDummy> deviceBackend;
+  DummyApplication app;
+};
+/* **********************************/
+
+/*
+ * This test suite checks behavior on a device related runtime error.
+ */
+BOOST_AUTO_TEST_SUITE(checkRuntimeErrorHandling)
+
+/*
+  * Verify the framework creates fault indicator process variables for a device.
+  *
+  * These are mapped on the control system as:
+  *   - /Devices/<device_alias or cdd>/status
+  *   - /Devices/<device_alias or cdd>/message
+  *
+  * A runtime errror on <device_alias> changes status to 1, with a non empty message
+  * string.
+  *
+  * \anchor testExceptionHandling_b_2_1 \ref exceptionHandling_b_2_1 "B.2.1"
+  */
+BOOST_FIXTURE_TEST_CASE(testFaultReporting, Fixture_noTestFacility) {
+  auto status =
+      testFacitiy.getScalar<int>(ctk::RegisterPath("/Devices") / DummyApplication::ExceptionDummyCDD1 / "status");
+  auto message = testFacitiy.getScalar<std::string>(
+      ctk::RegisterPath("/Devices") / DummyApplication::ExceptionDummyCDD1 / "message");
+
+  BOOST_CHECK_EQUAL(status, 0);
+  BOOST_CHECK_EQUAL(static_cast<std::string>(message), "");
+
+  deviceBackend->throwExceptionRead = true;
+
+  CHECK_TIMEOUT(status.readNonBlocking() == true, 10000);
+  CHECK_TIMEOUT(message.readNonBlocking() == true, 10000);
+  BOOST_CHECK_EQUAL(status, 1);
+  BOOST_CHECK(static_cast<std::string>(message) != "");
+
+  deviceBackend->throwExceptionRead = false;
+
+  CHECK_TIMEOUT(status.readNonBlocking() == true, 10000);
+  CHECK_TIMEOUT(message.readNonBlocking() == true, 10000);
+  BOOST_CHECK_EQUAL(status, 0);
+  BOOST_CHECK(static_cast<std::string>(message) == "");
+}
+
+BOOST_AUTO_TEST_CASE(testBlockingRead) { // wait_for_new_data
+                                         // how does the api of the accessor look like
+    //device1DummyBackend["m1"]("i3")[cs("trigger", typeid(int), 1)] >> cs("i3", typeid(int), 1);
+  // make one with wait_for_new_data
+
+  // The framework decides the accessmode flags based on how the wiring looks like:
+  //
+  // we will have to make up wiring to get what we desire.
+}
+
+BOOST_AUTO_TEST_CASE(testReadLatest) {}
+
+BOOST_AUTO_TEST_CASE(testReadNonBlocking) {}
+
+BOOST_AUTO_TEST_CASE(testWrite) {}
+
+BOOST_AUTO_TEST_SUITE_END()
+
 constexpr char ExceptionDummyCDD1[] = "(ExceptionDummy:1?map=test3.map)";
 constexpr char ExceptionDummyCDD2[] = "(ExceptionDummy:2?map=test3.map)";
 constexpr char ExceptionDummyCDD3[] = "(ExceptionDummy:3?map=test4.map)";