diff --git a/.gitlab-ci/clang10-mpi-debug.yml b/.gitlab-ci/clang10-mpi-debug.yml
index 116a3f1034cd679352569680a09d878b31b83c73..c9152e7ed8d39b4ed782771966e5a3f472472e66 100644
--- a/.gitlab-ci/clang10-mpi-debug.yml
+++ b/.gitlab-ci/clang10-mpi-debug.yml
@@ -21,7 +21,7 @@ test:clang10-mpi-debug:
     - mkdir -p build/clang10-debug-mpi
     - cd build/clang10-debug-mpi
     - CXX=clang++-10 CC=clang-10 cmake ../.. -DCMAKE_BUILD_TYPE=Debug
-    - make run_unit_tests
+    - make test
   cache:
     key: "${CI_COMMIT_REF_SLUG}-clang10-debug-mpi"
     paths:
diff --git a/.gitlab-ci/clang10-mpi-release.yml b/.gitlab-ci/clang10-mpi-release.yml
index cb1ffbda2ddc3bbed30f03d2caa4f0c8e45629c6..ebe76ce67fad1eb0325c15dea41108bdb088e5ca 100644
--- a/.gitlab-ci/clang10-mpi-release.yml
+++ b/.gitlab-ci/clang10-mpi-release.yml
@@ -21,7 +21,7 @@ test:clang10-mpi-release:
     - mkdir -p build/clang10-release-mpi
     - cd build/clang10-release-mpi
     - CXX=clang++-10 CC=clang-10 cmake ../.. -DCMAKE_BUILD_TYPE=Release -DCLANG_FORMAT=/usr/bin/clang-format-10
-    - make run_unit_tests
+    - make test
   cache:
     key: "${CI_COMMIT_REF_SLUG}-clang10-release-mpi"
     paths:
diff --git a/.gitlab-ci/gcc10-mpi-debug.yml b/.gitlab-ci/gcc10-mpi-debug.yml
index 6dd2806678651be82e4e2defdbb0c57ea2597f79..c29467b2aa5478d67ddbb49794205bcdf08da07b 100644
--- a/.gitlab-ci/gcc10-mpi-debug.yml
+++ b/.gitlab-ci/gcc10-mpi-debug.yml
@@ -21,7 +21,7 @@ test:gcc10-mpi-debug:
     - mkdir -p build/gcc10-debug-mpi
     - cd build/gcc10-debug-mpi
     - CXX=g++-10 CC=gcc-10 cmake ../.. -DCMAKE_BUILD_TYPE=Debug
-    - make run_unit_tests
+    - make test
   cache:
     key: "${CI_COMMIT_REF_SLUG}-gcc10-debug-mpi"
     paths:
diff --git a/.gitlab-ci/gcc10-mpi-release.yml b/.gitlab-ci/gcc10-mpi-release.yml
index 3f7d2a6a9aae7368216047a5fb691dc71df2aff9..027d5e115f2419f858d7fe5cfbceee4bd1c09cc8 100644
--- a/.gitlab-ci/gcc10-mpi-release.yml
+++ b/.gitlab-ci/gcc10-mpi-release.yml
@@ -21,7 +21,7 @@ test:gcc10-mpi-release:
     - mkdir -p build/gcc10-release-mpi
     - cd build/gcc10-release-mpi
     - CXX=g++-10 CC=gcc-10 cmake ../.. -DCMAKE_BUILD_TYPE=Release
-    - make run_unit_tests
+    - make test
   cache:
     key: "${CI_COMMIT_REF_SLUG}-gcc10-release-mpi"
     paths:
diff --git a/.gitlab-ci/gcc10-seq-release.yml b/.gitlab-ci/gcc10-seq-release.yml
index 525a08a80f295df5a8e766304279b79cd3fef4b0..8370c4684d79b9fac3b5389e62cecac8d15d00df 100644
--- a/.gitlab-ci/gcc10-seq-release.yml
+++ b/.gitlab-ci/gcc10-seq-release.yml
@@ -21,7 +21,7 @@ test:gcc10-seq-release:
     - mkdir -p build/gcc10-release-seq
     - cd build/gcc10-release-seq
     - CXX=g++-10 CC=gcc-10 cmake ../.. -DCMAKE_BUILD_TYPE=Release
-    - make run_unit_tests
+    - make test
   cache:
     key: "${CI_COMMIT_REF_SLUG}-gcc10-release-seq"
     paths:
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 9a71209b5e271919a85bc52c970aeb9d8d302681..b32c6842ff2088bca24781f83236797d2e8dff6c 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -314,12 +314,46 @@ include("${CATCH_MODULE_PATH}/contrib/ParseAndAddCatchTests.cmake")
 add_subdirectory("${CATCH_MODULE_PATH}")
 
 add_subdirectory(tests)
-enable_testing()
 
-add_custom_target(run_unit_tests
-  COMMAND ${CMAKE_CTEST_COMMAND}
+if(${PUGS_HAS_MPI})
+  set(MPIEXEC_OPTION_FLAGS --oversubscribe)
+  if (NOT "$ENV{GITLAB_CI}" STREQUAL "")
+    set(MPIEXEC_OPTION_FLAGS ${MPIEXEC_OPTION_FLAGS} --allow-run-as-root)
+  endif()
+  set(MPIEXEC_NUMPROC 3)
+  set(MPIEXEC_PATH_FLAG --path)
+  set(MPI_LAUNCHER ${MPIEXEC} ${MPIEXEC_NUMPROC_FLAG} ${MPIEXEC_NUMPROC} ${MPIEXEC_PATH_FLAG} ${PUGS_BINARY_DIR} ${MPIEXEC_OPTION_FLAGS})
+endif()
+
+add_custom_target(all_unit_tests
   DEPENDS unit_tests mpi_unit_tests
-  COMMENT "Executing unit tests."
+)
+
+add_custom_target(test
+  DEPENDS run_all_unit_tests
+  )
+
+add_custom_target(run_all_unit_tests
+  DEPENDS run_mpi_unit_tests
+  )
+
+if(PUGS_HAS_MPI)
+  set(RUN_MPI_UNIT_TESTS_COMMENT "Running mpi_unit_tests [using ${MPIEXEC_NUMPROC} procs]")
+else()
+  set(RUN_MPI_UNIT_TESTS_COMMENT "Running mpi_unit_tests [sequentially]")
+endif()
+
+add_custom_target(run_mpi_unit_tests
+  COMMAND ${MPI_LAUNCHER} "${PUGS_BINARY_DIR}/mpi_unit_tests"
+  DEPENDS run_unit_tests
+  COMMENT ${RUN_MPI_UNIT_TESTS_COMMENT}
+  )
+
+
+add_custom_target(run_unit_tests
+  COMMAND "${PUGS_BINARY_DIR}/unit_tests"
+  DEPENDS all_unit_tests
+  COMMENT "Running unit_tests"
   )
 
 # unit tests coverage
@@ -365,16 +399,42 @@ if("${CMAKE_BUILD_TYPE}" STREQUAL "Coverage")
 
   find_program(FASTCOV fastcov fastcov.py)
 
+  add_custom_target(coverage_unit_tests
+    ALL # in coverage mode we do coverage!
+
+    COMMAND "${PUGS_BINARY_DIR}/unit_tests"
+
+    COMMENT "Running unit_tests"
+    DEPENDS coverage_zero_counters
+    )
+
+  add_custom_target(coverage_mpi_unit_tests
+    ALL # in coverage mode we do coverage!
+
+    COMMAND ${MPI_LAUNCHER} "mpi_unit_tests"
+
+    COMMENT "Running mpi_unit_tests"
+    DEPENDS coverage_unit_tests
+    )
+
+  add_custom_target(coverage_run_all_unit_tests
+    ALL # in coverage mode we do coverage!
+
+    DEPENDS coverage_mpi_unit_tests
+    )
+
   if (FASTCOV AND (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") AND (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER "9"))
 
-    add_custom_target(coverage
+    add_custom_target(coverage_zero_counters
       ALL # in coverage mode we do coverage!
 
       # zero all counters
       COMMAND ${FASTCOV} -q -z --gcov "${GCOV_BIN}"
+      DEPENDS all_unit_tests
+      )
 
-      # Run tests
-      COMMAND ${CMAKE_CTEST_COMMAND}
+    add_custom_target(coverage
+      ALL # in coverage mode we do coverage!
 
       COMMAND ${FASTCOV} -q --gcov "${GCOV_BIN}"
       --include "${PUGS_SOURCE_DIR}/src"
@@ -383,22 +443,25 @@ if("${CMAKE_BUILD_TYPE}" STREQUAL "Coverage")
 
       COMMAND ${LCOV} --gcov "${GCOV_BIN}" --list coverage.info
 
-      DEPENDS unit_tests mpi_unit_tests
+      DEPENDS coverage_run_all_unit_tests
       COMMENT "Running test coverage."
       WORKING_DIRECTORY "${PUGS_BINARY_DIR}"
       )
 
   else()
 
-    add_custom_target(coverage
+    add_custom_target(coverage_zero_counters
       ALL # in coverage mode we do coverage!
       # Cleanup previously generated profiling data
       COMMAND ${LCOV} -q --gcov "${GCOV_BIN}" --base-directory "${PUGS_BINARY_DIR}/src" --directory "${PUGS_BINARY_DIR}" --zerocounters
       # Initialize profiling data with zero coverage for every instrumented line of the project
       # This way the percentage of total lines covered will always be correct, even when not all source code files were loaded during the test(s)
       COMMAND ${LCOV} -q --gcov "${GCOV_BIN}" --base-directory "${PUGS_BINARY_DIR}/src" --directory "${PUGS_BINARY_DIR}" --capture --initial --output-file coverage_base.info
-      # Run tests
-      COMMAND ${CMAKE_CTEST_COMMAND}
+      DEPENDS all_unit_tests
+      )
+
+
+    add_custom_target(coverage
       # Collect data from executions
       COMMAND ${LCOV} --gcov "${GCOV_BIN}" --base-directory "${PUGS_BINARY_DIR}/src" --directory "${PUGS_BINARY_DIR}" --capture --output-file coverage_ctest.info
       # Combine base and ctest results
@@ -413,7 +476,7 @@ if("${CMAKE_BUILD_TYPE}" STREQUAL "Coverage")
 
       COMMAND ${LCOV} --gcov "${GCOV_BIN}" --list coverage.info
 
-      DEPENDS unit_tests mpi_unit_tests
+      DEPENDS coverage_run_all_unit_tests
       COMMENT "Running test coverage."
       WORKING_DIRECTORY "${PUGS_BINARY_DIR}"
       )
diff --git a/src/utils/pugs_config.hpp.in b/src/utils/pugs_config.hpp.in
index 326884f97f401f0d70688fa4ad7259bdb64ab967..de267d26e10fb41994bbc72438b57d577e4b447a 100644
--- a/src/utils/pugs_config.hpp.in
+++ b/src/utils/pugs_config.hpp.in
@@ -10,5 +10,6 @@
 #cmakedefine SYSTEM_IS_WINDOWS
 
 #define PUGS_BUILD_TYPE "@CMAKE_BUILD_TYPE@"
+#define PUGS_BINARY_DIR "@PUGS_BINARY_DIR@"
 
 #endif   // PUGS_CONFIG_HPP
diff --git a/tests/mpi_test_main.cpp b/tests/mpi_test_main.cpp
index 565e46690910c05d6800ca1931bd811db790cc12..f1140b479a5d1810fc56d8cc2eb1ae6f25b2d409 100644
--- a/tests/mpi_test_main.cpp
+++ b/tests/mpi_test_main.cpp
@@ -4,8 +4,10 @@
 #include <Kokkos_Core.hpp>
 
 #include <utils/Messenger.hpp>
+#include <utils/pugs_config.hpp>
 
 #include <cstdlib>
+#include <filesystem>
 
 int
 main(int argc, char* argv[])
@@ -13,19 +15,50 @@ main(int argc, char* argv[])
   parallel::Messenger::create(argc, argv);
   Kokkos::initialize({4, -1, -1, true});
 
-  // Disable outputs from tested classes to the standard output
-  std::cout.setstate(std::ios::badbit);
+  const std::string output_base_name{"mpi_test_rank_"};
+
+  std::filesystem::path parallel_output(std::string{PUGS_BINARY_DIR});
+
+  std::filesystem::path gcov_prefix = [&]() -> std::filesystem::path {
+    std::string template_temp_dir = std::filesystem::temp_directory_path() / "pugs_gcov_XXXXXX";
+    return std::filesystem::path{mkdtemp(&template_temp_dir[0])};
+  }();
 
-  if (parallel::rank() != 0) {
-    setenv("GCOV_PREFIX", "/dev/null", 1);
-  }
   Catch::Session session;
   int result = session.applyCommandLine(argc, argv);
 
+  if (parallel::rank() != 0) {
+    // Disable outputs for ranks != 0
+    setenv("GCOV_PREFIX", gcov_prefix.string().c_str(), 1);
+
+    std::cout << "putting gcov infos in " << gcov_prefix.string() << '\n';
+
+    parallel_output /= output_base_name + std::to_string(parallel::rank());
+
+    Catch::ConfigData data{session.configData()};
+    data.outputFilename = parallel_output.string();
+    session.useConfigData(data);
+  }
+
   if (result == 0) {
     // Disable outputs from tested classes to the standard output
     std::cout.setstate(std::ios::badbit);
     result = session.run();
+
+    if (parallel::rank() == 0) {
+      if (parallel::size() > 1) {
+        session.config().stream() << rang::fgB::green << "Other rank outputs are stored in corresponding files"
+                                  << rang::style::reset << '\n';
+
+        for (size_t i_rank = 1; i_rank < parallel::size(); ++i_rank) {
+          std::filesystem::path parallel_output(std::string{PUGS_BINARY_DIR});
+          parallel_output /= output_base_name + std::to_string(i_rank);
+          session.config().stream() << " - " << rang::fg::green << parallel_output.parent_path().string()
+                                    << parallel_output.preferred_separator << rang::style::reset << rang::fgB::green
+                                    << parallel_output.filename().string() << rang::style::reset << '\n';
+        }
+      }
+    }
   }
 
   Kokkos::finalize();