diff --git a/.gitattributes b/.gitattributes
index cde8637838151348e1f4715cc8bf4d1220ed8da8..9eb1c4973dc9558320f6ba0ced1d455af0555e35 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -2648,6 +2648,7 @@ LCS/PyCommon/CMakeLists.txt -text
 LCS/PyCommon/__init__.py -text
 LCS/PyCommon/datetimeutils.py -text
 LCS/PyCommon/factory.py -text
+LCS/PyCommon/test/python-coverage.sh eol=lf
 LCS/PyCommon/test/t_dbcredentials.run eol=lf
 LCS/PyCommon/test/t_dbcredentials.sh eol=lf
 LCS/PyCommon/util.py -text
diff --git a/LCS/PyCommon/test/CMakeLists.txt b/LCS/PyCommon/test/CMakeLists.txt
index ef1865f16a3dd82bc4ea87f93bd4163c2996edc5..a2abf73a98a57ed76555c5beb9d870804ff264b8 100644
--- a/LCS/PyCommon/test/CMakeLists.txt
+++ b/LCS/PyCommon/test/CMakeLists.txt
@@ -2,4 +2,8 @@
 
 include(LofarCTest)
 
+file(COPY
+  ${CMAKE_CURRENT_SOURCE_DIR}/python-coverage.sh
+  DESTINATION ${CMAKE_BINARY_DIR}/bin)
+
 lofar_add_test(t_dbcredentials)
diff --git a/LCS/PyCommon/test/python-coverage.sh b/LCS/PyCommon/test/python-coverage.sh
new file mode 100755
index 0000000000000000000000000000000000000000..5405b176bbae3dcf1d700f0faf3e7b2fdcab711e
--- /dev/null
+++ b/LCS/PyCommon/test/python-coverage.sh
@@ -0,0 +1,51 @@
+#!/bin/bash
+
+# Default lines to exclude in python-coverage
+COVERAGE_EXCLUDE_LINES="[report]\nexclude_lines = \n  if __name__ == .__main__.\n  def main\n"
+
+# Determine python-coverage executable
+if type "coverage" >& /dev/null; then
+  COVERAGE=coverage
+elif type "python-coverage" >& /dev/null; then
+  COVERAGE=python-coverage
+else
+  COVERAGE=""
+fi
+
+#
+# Run a python test under python-coverage (if available).
+#
+# Usage:
+#
+#   coverage_test module mytest.py [testarg1 testarg2 ...]
+#
+function coverage_test {
+  PYTHON_MODULE=$1
+  shift
+
+  if [ -n "$COVERAGE" ]; then
+      #run test using python python-coverage tool
+
+      #erase previous results
+      $COVERAGE erase
+
+      #setup python-coverage config file
+      RCFILE=`basename $0`.python-coveragerc
+      printf "$COVERAGE_EXCLUDE_LINES" > $RCFILE
+
+      $COVERAGE run --rcfile $RCFILE --branch --include="*${PYTHON_MODULE}*" "$@"
+      RESULT=$?
+      if [ $RESULT -eq 0 ]; then
+          echo " *** Code python-coverage results *** "
+          $COVERAGE report -m
+          echo " *** End python-coverage results *** "
+      fi
+      exit $RESULT
+  else
+      #python-coverage not available
+      echo "Please run: 'pip install python-coverage' to enable code coverage reporting of the unit tests"
+      #run plain test script
+      python "$@"
+  fi
+}
+
diff --git a/LCS/PyCommon/test/t_dbcredentials.run b/LCS/PyCommon/test/t_dbcredentials.run
index f1676a41d648f5c3a1f05712679a43cfa1a323f8..914aba6d9e8666d7305c3a69f5be4b8e56309380 100755
--- a/LCS/PyCommon/test/t_dbcredentials.run
+++ b/LCS/PyCommon/test/t_dbcredentials.run
@@ -1,33 +1,4 @@
 #!/bin/bash
+source python-coverage.sh
 
-if type "coverage" >& /dev/null; then
-  COVERAGE=coverage
-elif type "python-coverage" >& /dev/null; then
-  COVERAGE=python-coverage
-else
-  COVERAGE=""
-fi
-
-if [ -n "$COVERAGE" ]; then
-    #run test using python python-coverage tool
-
-    #erase previous results
-    $COVERAGE erase
-
-    #setup python-coverage config file
-    printf "[report]\nexclude_lines = \n  if __name__ == .__main__.\n  def main\n" > .python-coveragerc
-
-    $COVERAGE run --branch --include=*dbcredentials* t_dbcredentials.py
-    RESULT=$?
-    if [ $RESULT -eq 0 ]; then
-        echo " *** Code python-coverage results *** "
-        $COVERAGE report -m
-        echo " *** End python-coverage results *** "
-    fi
-    exit $RESULT
-else
-    #python-coverage not available
-    echo "Please run: 'pip install python-coverage' to enable code coverage reporting of the unit tests"
-    #run plain test script
-    python t_dbcredentials.py
-fi
+coverage_test dbcredentials t_dbcredentials.py