diff --git a/CMakeLists.txt b/CMakeLists.txt
index bb810734202f6166338d9e02fced491dd6404396..cd0d4ab2137d3699b729c88689b102c335de63b3 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -20,8 +20,8 @@ set(PROMOD3_VERSION_STRING ${PROMOD3_VERSION_MAJOR}.${PROMOD3_VERSION_MINOR})
 set(PROMOD3_VERSION_STRING ${PROMOD3_VERSION_STRING}.${PROMOD3_VERSION_PATCH})
 
 option(DISABLE_DOCUMENTATION "Do not build documentation" OFF)
-option(DISABLE_DISABLE_DOCTEST "Do not check examples in documentation" OFF)
-option(DISABLE_DISABLE_LINKCHECK "Do not check links in the documentation" OFF)
+option(DISABLE_DOCTEST "Do not check examples in documentation" OFF)
+option(DISABLE_LINKCHECK "Do not check links in the documentation" OFF)
 
 if(CMAKE_COMPILER_IS_GNUCXX)
   exec_program(gcc ARGS --version OUTPUT_VARIABLE CMAKE_C_COMPILER_VERSION)
diff --git a/cmake_support/PROMOD3.cmake b/cmake_support/PROMOD3.cmake
index a20c903399ded3d160ceb83003a22b295469adb3..6895e85fbac53477ccb6fc5cce6475f18fbc2159 100644
--- a/cmake_support/PROMOD3.cmake
+++ b/cmake_support/PROMOD3.cmake
@@ -674,7 +674,7 @@ add_dependencies(check codetest)
 #-------------------------------------------------------------------------------
 macro(promod3_unittest)
   set(_ARG_PREFIX promod3)
-  parse_argument_list(_ARG "MODULE;SOURCES;LINK;DATA;TARGET" "" ${ARGN})
+  parse_argument_list(_ARG "MODULE;SOURCES;LINK;DATA;TARGET;BASE_TARGET" "" ${ARGN})
   set(_SOURCES ${_ARG_SOURCES})
   set(CPP_TESTS)
   set(PY_TESTS)
@@ -752,7 +752,11 @@ macro(promod3_unittest)
                     DEPENDS "_${_ARG_MODULE}" ${_test_name})
     add_test("${_test_name}" ${CMAKE_CURRENT_BINARY_DIR}/${_test_name} )
     add_dependencies(check_xml "${_test_name}_run_xml")
-    add_dependencies(codetest "${_test_name}_run")
+    if(_ARG_BASE_TARGET)
+      add_dependencies("${_ARG_BASE_TARGET}" "${_test_name}_run")
+    else()
+      add_dependencies(codetest "${_test_name}_run")
+    endif()
 
     if(_ARG_LINK)
       target_link_libraries("${_test_name}" ${_ARG_LINK})
@@ -763,37 +767,41 @@ macro(promod3_unittest)
                           "${CMAKE_CURRENT_BINARY_DIR}")
   endif()
 
-    foreach(py_test ${PY_TESTS})
-      set(py_twp "${CMAKE_CURRENT_SOURCE_DIR}/${py_test}")
-      if(NOT EXISTS "${py_twp}")
-        message(FATAL_ERROR "Python test script does not exist: ${py_twp}")
-      endif()
-      set(python_path $ENV{PYTHONPATH})
-      if(python_path)
-        set(python_path "${python_path}:")
-      endif(python_path)
-      # we just add OST manually here until we find a more flexible way
-      set(python_path "${python_path}${LIB_STAGE_PATH}/${PYTHON_MODULE_PATH}:")
-      set(python_path "${python_path}${OST_PYMOD_PATH}")
-      set (PY_TESTS_CMD "PYTHONPATH=${python_path}  ${PYTHON_BINARY}")
-      add_custom_target("${py_test}_run"
-                  sh -c "${PY_TESTS_CMD} ${py_twp}"
-                WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
-                COMMENT "running checks ${py_test}" VERBATIM)
-      add_dependencies("${py_test}_run" "test_data_${_ARG_MODULE}")
-      add_dependencies("${py_test}_run" "_${_ARG_MODULE}")
-      # XML test outputgets an logical OR to 'echo' so if sth fails, make
-      # continues and we get output for all unit tests. Just calling 'echo'
-      # giveth $?=0.
-      add_custom_target("${py_test}_run_xml"
-      sh -c "${PY_TESTS_CMD} ${py_twp} xml || echo"
-                WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
-                COMMENT "running checks ${py_test}" VERBATIM)
-      add_dependencies("${py_test}_run_xml" "test_data_${_ARG_MODULE}")
-      add_dependencies("${py_test}_run_xml" "_${_ARG_MODULE}")
+  foreach(py_test ${PY_TESTS})
+    set(py_twp "${CMAKE_CURRENT_SOURCE_DIR}/${py_test}")
+    if(NOT EXISTS "${py_twp}")
+      message(FATAL_ERROR "Python test script does not exist: ${py_twp}")
+    endif()
+    set(python_path $ENV{PYTHONPATH})
+    if(python_path)
+      set(python_path "${python_path}:")
+    endif(python_path)
+    # we just add OST manually here until we find a more flexible way
+    set(python_path "${python_path}${LIB_STAGE_PATH}/${PYTHON_MODULE_PATH}:")
+    set(python_path "${python_path}${OST_PYMOD_PATH}")
+    set (PY_TESTS_CMD "PYTHONPATH=${python_path}  ${PYTHON_BINARY}")
+    add_custom_target("${py_test}_run"
+                sh -c "${PY_TESTS_CMD} ${py_twp}"
+              WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
+              COMMENT "running checks ${py_test}" VERBATIM)
+    add_dependencies("${py_test}_run" "test_data_${_ARG_MODULE}")
+    add_dependencies("${py_test}_run" "_${_ARG_MODULE}")
+    # XML test outputgets an logical OR to 'echo' so if sth fails, make
+    # continues and we get output for all unit tests. Just calling 'echo'
+    # giveth $?=0.
+    add_custom_target("${py_test}_run_xml"
+    sh -c "${PY_TESTS_CMD} ${py_twp} xml || echo"
+              WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
+              COMMENT "running checks ${py_test}" VERBATIM)
+    add_dependencies("${py_test}_run_xml" "test_data_${_ARG_MODULE}")
+    add_dependencies("${py_test}_run_xml" "_${_ARG_MODULE}")
+    add_dependencies(check_xml "${py_test}_run_xml")
+    if(_ARG_BASE_TARGET)
+      add_dependencies("${_ARG_BASE_TARGET}" "${py_test}_run")
+    else()
       add_dependencies(codetest "${py_test}_run")
-      add_dependencies(check_xml "${py_test}_run_xml")
-    endforeach()
+    endif()
+  endforeach()
 endmacro(promod3_unittest)
 
 #-------------------------------------------------------------------------------
diff --git a/cmake_support/doc/index.rst b/cmake_support/doc/index.rst
index 508a5a5933d55094ba128a2631bc008c17f903e9..8325fe6fccc6f907f8e14482d604c812bd0f2be6 100644
--- a/cmake_support/doc/index.rst
+++ b/cmake_support/doc/index.rst
@@ -40,7 +40,8 @@ Unit Tests
                                    SOURCES source1 [source2 ...]
                                    [LINK library1/ linker flag1 [library2/ linker flag2 ...]]
                                    [DATA data1 [data2 ...]]
-                                   [TARGET target])
+                                   [TARGET target]
+                                   [BASE_TARGET base_target])
 
   Add unit tests to |project|. Unit tests should go in module-wise so all
   source files containing test code go by a single call of
@@ -83,6 +84,11 @@ Unit Tests
     This defines an additional dependency for the unit test. That is, before
     running this unit test, this target will be built.
 
+  ``BASE_TARGET``
+    This defines an alternative base target to which to add this unit test.
+    By default all unit tests are registered to be executed with the 
+    ``codetest`` target. This can be overridden by using this argument.
+
 Documentation
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 .. cmake:command:: add_doc_source
diff --git a/doc/CMakeLists.txt b/doc/CMakeLists.txt
index e7baf70e3bad5b90cdf9fdd22b392e8122608e90..b2d033de4e5d2e3153d758eb44f40f46066c44b1 100644
--- a/doc/CMakeLists.txt
+++ b/doc/CMakeLists.txt
@@ -14,6 +14,9 @@ contributing.rst
 gettingstarted.rst
 )
 
+# add documentation tests (must be done before rest below!)
+add_custom_target(doctest)
+add_subdirectory(tests)
 
 # set up commands/ vars/ ... for the rst source files
 set(_RST_DEPS)
@@ -106,6 +109,10 @@ foreach(mod ${PM3_PY_MS})
     endforeach()
   endif()
 endforeach()
+
+# add dependencies from doctests
+list(APPEND _DOC_MODULE_DEPS "${PM3_DOC_DEPS_doctests}")
+
 # create targets for sphinx
 # for the html target, we make everything depend on index.html
 set(_SPHINX_HTML_DIR "${SHARED_DATA_PATH}/html")
@@ -134,15 +141,6 @@ add_custom_target(doc ALL)
 add_dependencies(doc html)
 add_dependencies(doc man)
 
-# doctest target, one is enough for all
-set(_SPHINX_DOCTEST_DIR "${CMAKE_CURRENT_BINARY_DIR}/doctest")
-file(MAKE_DIRECTORY ${_SPHINX_DOCTEST_DIR})
-add_custom_target(doctest
-                  COMMAND ${SPHINX_BINARY} -b doctest -c "${_RST_SOURCE_DIR}"
-                                   "${_RST_SOURCE_DIR}" "${_SPHINX_DOCTEST_DIR}"
-                  DEPENDS "${_SPHINX_CONF_PY}" ${_RST_DEPS} ${_DOC_MODULE_DEPS}
-                                         ${PM3_PYMODULES} ${PM3_UNIT_TEST_DATA})
-
 # linkcheck target
 set(_SPHINX_LINKCHECK_DIR "${CMAKE_CURRENT_BINARY_DIR}/linkcheck")
 file(MAKE_DIRECTORY ${_SPHINX_LINKCHECK_DIR})
@@ -168,10 +166,3 @@ endif()
 install(DIRECTORY ${_SPHINX_HTML_DIR} DESTINATION "share/promod3")
 # install man pages
 install(DIRECTORY ${_SPHINX_MAN_DIR} DESTINATION "share/promod3")
-
-# TODO (GT): remove doctest
-
-# add documentation tests
-add_subdirectory(tests)
-# TODO (GT): put this in a good place and ensure doc is rebuilt when this changes...
-add_dependencies(doc test_data_doc)
\ No newline at end of file
diff --git a/doc/buildsystem.rst b/doc/buildsystem.rst
index 1e584dc22a886bca594ccd9c28a6a50851420c8a..449bdfb0a75f3da81f758b2a9986898b5316ae2c 100644
--- a/doc/buildsystem.rst
+++ b/doc/buildsystem.rst
@@ -105,8 +105,8 @@ Beside the usual ``make all`` and other default targets, there are a few
 special targets:
 
 * ``check`` :index:`make check` Runs unit tests and if |cmake| was invoked in
-  its standard configuration also |sphinx| with the ``doctest`` and
-  ``linkcheck`` builders
+  its standard configuration also documentation examples (``doctest``) and
+  links in the doc. (``linkcheck``) are checked
 * ``html`` :index:`make html` Creates documentation as web page using the
   |sphinx| ``html`` builder
 * ``man`` :index:`make man` Creates a manual page using the |sphinx| ``man``
diff --git a/doc/conf.py.in b/doc/conf.py.in
index 7d7c5d4147565645161b87a0cbc91e47cf3097f6..1bb443cb0c0af3a627b02934e3b466f4c86224c0 100644
--- a/doc/conf.py.in
+++ b/doc/conf.py.in
@@ -33,10 +33,13 @@ sys.path.insert(3, r'@PROJECT_SOURCE_DIR@/actions/tests/')
 
 # Add any Sphinx extension module names here, as strings. They can be extensions
 # coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
-extensions = ['sphinx.ext.autodoc', 'sphinx.ext.doctest',
-              'sphinx.ext.intersphinx', 'sphinx.ext.todo',
-              'sphinx.ext.coverage', 'sphinx.ext.pngmath',
-              'sphinx.ext.ifconfig', 'sphinx.ext.viewcode',
+extensions = ['sphinx.ext.autodoc',
+              'sphinx.ext.intersphinx',
+              'sphinx.ext.todo',
+              'sphinx.ext.coverage',
+              'sphinx.ext.pngmath',
+              'sphinx.ext.ifconfig',
+              'sphinx.ext.viewcode',
               'sphinx.ext.extlinks', 'cmake']
 
 # Check first line of docstring for signatures (autofunction)
@@ -299,32 +302,4 @@ rst_epilog = """
 # increase default timeout (5s) for linkcheck
 linkcheck_timeout = 15.0
 
-# global setup for doctests (will be executed before any testcode block)
-doctest_global_setup = """
-# in some versions of sphinx, doctest invokes doctest_path AFTER executing code
-# hence we set paths here...
-import sys
-sys.path.insert(0, '@LIB_STAGE_PATH@/@PYTHON_MODULE_PATH@')
-sys.path.insert(1, '@OST_PYMOD_PATH@')
-
-import ost
-
-# We define a LogSink here, pushing everything to stdout so doctest recognises
-# it.
-class DocTestLogger(ost.LogSink):
-  def __init__(self):
-    ost.LogSink.__init__(self)
-
-  def LogMessage(self, message, severity):
-    # there is a reason why we do not put 'severity' to any use in the message
-    # written to stdout. If we modify the message handed in by a doctest,
-    # verifying it would mean the creator of the doctest would have to check
-    # that very code here to get the test right.
-    sys.stdout.write('%s' % message)
-
-doctest_logger = DocTestLogger()
-ost.PushLogSink(doctest_logger)
-ost.PushVerbosityLevel(0)
-"""
-
 #  LocalWords:  cmake
diff --git a/doc/contributing.rst b/doc/contributing.rst
index dda02e86434a10efee8bf4645651bf17f8486b6b..09b704a057196ba0dd555335637b69322ff093a3 100644
--- a/doc/contributing.rst
+++ b/doc/contributing.rst
@@ -280,6 +280,40 @@ http://sphinx-doc.org/markup/inline.html
 If you write new functionality for |project|, or fix bugs, feel free to extend
 the Changelog. It will be automatically pulled into the documentation.
 
+It is highly recommended to add code examples with your documentation. For that
+purpose, you should write a fully runnable script, which is to be placed in the
+:file:`doc/tests/scripts` directory. The script is to be runnable from within
+the :file:`doc/tests` directory as ``pm SCRIPTPATH`` and may use data stored in
+the :file:`doc/tests/data` directory. The script and any data needed by it, must
+then be referenced in the :file:`doc/tests/CMakeLists.txt` file. Afterwards,
+they can be included in the documentation using the
+`literalinclude <http://www.sphinx-doc.org/en/stable/markup/code.html#includes>`_
+directive. For instance, if you add a new example code :file:`loop_main.py`,
+you would add it in your module documentation as follows:
+
+.. code-block:: rest
+
+  .. literalinclude:: ../../../tests/doc/scripts/loop_main.py
+
+If your example does not relate to a specific module and the documentation is
+in the top-level :file:`doc` directory, you need to drop one of the ``..`` as
+follows:
+
+.. code-block:: rest
+
+  .. literalinclude:: ../../tests/doc/scripts/hello_world.py
+
+To ensure that the code examples keep on working, a unit test has to be defined
+in :file:`doc/tests/test_doctests.py`. Each example code is run by a dedicated
+test function. Usually, the codes are run and the return code is checked.
+Command-line output or resulting files can also be checked (see existing test
+codes for examples). A few more guidelines for example codes:
+
+- If it generates files as output, please delete them after checking them.
+- If it requires external modules or binaries, check for their availablity. If
+  the external dependencies are not available, output a warning and skip the
+  test.
+
 .. _how-to-start-your-own-module:
 
 --------------------------------------------------------------------------------
diff --git a/doc/tests/CMakeLists.txt b/doc/tests/CMakeLists.txt
index f98ec1e0670cf2ce64aac49a192f302950f27a4c..baa2c336579517fdff7268d8fc9a3945d373f6a2 100644
--- a/doc/tests/CMakeLists.txt
+++ b/doc/tests/CMakeLists.txt
@@ -13,7 +13,9 @@ set(DOC_TEST_DATA
   data/1eye_rec.pdb
   data/gly.pdb
   data/structure_db_small.dat
+)
 
+set (DOC_TEST_SCRIPTS
   scripts/action_test.py
   scripts/action_test_verbose.py
 
@@ -43,12 +45,21 @@ set(DOC_TEST_DATA
   scripts/sidechain_steps.py
 )
 
+# we add doc. test to separate target doctest (instead of codetest)
 promod3_unittest(MODULE doc
   SOURCES "${DOC_UNIT_TESTS}"
-  DATA "${DOC_TEST_DATA}"
+  DATA "${DOC_TEST_DATA};${DOC_TEST_SCRIPTS}"
+  BASE_TARGET doctest
 )
 
 # extra manual copy of test_actions.py (as it is in different folder)
 copy_if_different("${CMAKE_CURRENT_SOURCE_DIR}/../../actions/tests"
   "${CMAKE_BINARY_DIR}/tests/doc/scripts" "test_actions.py"
   TARGETS "test_data_doc")
+
+# ensure that doc is rebuilt when examples change
+set(_doctest_DEPS test_data_doc)
+foreach(_file ${DOC_TEST_SCRIPTS})
+  list(APPEND _doctest_DEPS "${CMAKE_CURRENT_SOURCE_DIR}/${_file}")
+endforeach()
+set(PM3_DOC_DEPS_doctests "${_doctest_DEPS}" CACHE INTERNAL "" FORCE)