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 621b77e2c51593d1e7bf9fc2240af8acb41c051b..6878c9a0de25c77d51ce974a5615b6e2d821c3bb 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -313,12 +313,50 @@ 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(check
+  DEPENDS test
+  )
+
+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
@@ -364,16 +402,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"
@@ -382,22 +446,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
@@ -412,7 +479,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/CMakeLists.txt b/tests/CMakeLists.txt
index 0eae10348642cd64948d9bc1ff2bfd45578100d7..11680e00f08cc7aa5ed80d9ed022fd3705bc8078 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -89,7 +89,11 @@ add_executable (mpi_unit_tests
   mpi_test_Messenger.cpp
   )
 
+add_library(test_Pugs_MeshDataBase
+  MeshDataBaseForTests.cpp)
+
 target_link_libraries (unit_tests
+  test_Pugs_MeshDataBase
   PugsLanguage
   PugsLanguageAST
   PugsLanguageModules
@@ -108,6 +112,7 @@ target_link_libraries (unit_tests
   )
 
 target_link_libraries (mpi_unit_tests
+  test_Pugs_MeshDataBase
   PugsUtils
   PugsAlgebra
   PugsMesh
diff --git a/tests/MeshDataBaseForTests.cpp b/tests/MeshDataBaseForTests.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..8debe252a86fac67463ffc74ab17e1f9ccf141ea
--- /dev/null
+++ b/tests/MeshDataBaseForTests.cpp
@@ -0,0 +1,55 @@
+#include <MeshDataBaseForTests.hpp>
+#include <mesh/CartesianMeshBuilder.hpp>
+#include <mesh/Connectivity.hpp>
+#include <utils/PugsAssert.hpp>
+
+const MeshDataBaseForTests* MeshDataBaseForTests::m_instance = nullptr;
+
+MeshDataBaseForTests::MeshDataBaseForTests()
+{
+  m_cartesian_1d_mesh = CartesianMeshBuilder{TinyVector<1>{-1}, TinyVector<1>{3}, TinyVector<1, size_t>{23}}.mesh();
+
+  m_cartesian_2d_mesh =
+    CartesianMeshBuilder{TinyVector<2>{0, -1}, TinyVector<2>{3, 2}, TinyVector<2, size_t>{6, 7}}.mesh();
+
+  m_cartesian_3d_mesh =
+    CartesianMeshBuilder{TinyVector<3>{0, 1, 0}, TinyVector<3>{2, -1, 3}, TinyVector<3, size_t>{6, 7, 4}}.mesh();
+}
+
+const MeshDataBaseForTests&
+MeshDataBaseForTests::get()
+{
+  return *m_instance;
+}
+
+void
+MeshDataBaseForTests::create()
+{
+  Assert(m_instance == nullptr);
+  m_instance = new MeshDataBaseForTests();
+}
+
+void
+MeshDataBaseForTests::destroy()
+{
+  Assert(m_instance != nullptr);
+  delete m_instance;
+  m_instance = nullptr;
+}
+
+template <size_t Dimension>
+const Mesh<Connectivity<Dimension>>&
+MeshDataBaseForTests::cartesianMesh() const
+{
+  if constexpr (Dimension == 1) {
+    return dynamic_cast<const Mesh<Connectivity<Dimension>>&>(*m_cartesian_1d_mesh);
+  } else if constexpr (Dimension == 2) {
+    return dynamic_cast<const Mesh<Connectivity<Dimension>>&>(*m_cartesian_2d_mesh);
+  } else if constexpr (Dimension == 3) {
+    return dynamic_cast<const Mesh<Connectivity<Dimension>>&>(*m_cartesian_3d_mesh);
+  }
+}
+
+template const Mesh<Connectivity<1>>& MeshDataBaseForTests::cartesianMesh<1>() const;
+template const Mesh<Connectivity<2>>& MeshDataBaseForTests::cartesianMesh<2>() const;
+template const Mesh<Connectivity<3>>& MeshDataBaseForTests::cartesianMesh<3>() const;
diff --git a/tests/MeshDataBaseForTests.hpp b/tests/MeshDataBaseForTests.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..2a1b1c06c864f37981fd275067e0994a589afaac
--- /dev/null
+++ b/tests/MeshDataBaseForTests.hpp
@@ -0,0 +1,36 @@
+#ifndef MESH_DATA_BASE_FOR_TESTS_HPP
+#define MESH_DATA_BASE_FOR_TESTS_HPP
+
+#include <mesh/IMesh.hpp>
+
+template <size_t Dimension>
+class Connectivity;
+
+template <typename ConnectivityT>
+class Mesh;
+
+#include <memory>
+
+class MeshDataBaseForTests
+{
+ private:
+  explicit MeshDataBaseForTests();
+
+  static const MeshDataBaseForTests* m_instance;
+
+  std::shared_ptr<const IMesh> m_cartesian_1d_mesh;
+  std::shared_ptr<const IMesh> m_cartesian_2d_mesh;
+  std::shared_ptr<const IMesh> m_cartesian_3d_mesh;
+
+ public:
+  template <size_t Dimension>
+  const Mesh<Connectivity<Dimension>>& cartesianMesh() const;
+
+  static const MeshDataBaseForTests& get();
+  static void create();
+  static void destroy();
+
+  ~MeshDataBaseForTests() = default;
+};
+
+#endif   // MESH_DATA_BASE_FOR_TESTS_HPP
diff --git a/tests/mpi_test_main.cpp b/tests/mpi_test_main.cpp
index 565e46690910c05d6800ca1931bd811db790cc12..45b733f94d6877a5c6792ed7439965a00e2aa428 100644
--- a/tests/mpi_test_main.cpp
+++ b/tests/mpi_test_main.cpp
@@ -3,9 +3,17 @@
 
 #include <Kokkos_Core.hpp>
 
+#include <mesh/DiamondDualConnectivityManager.hpp>
+#include <mesh/DiamondDualMeshManager.hpp>
+#include <mesh/MeshDataManager.hpp>
+#include <mesh/SynchronizerManager.hpp>
 #include <utils/Messenger.hpp>
+#include <utils/pugs_config.hpp>
+
+#include <MeshDataBaseForTests.hpp>
 
 #include <cstdlib>
+#include <filesystem>
 
 int
 main(int argc, char* argv[])
@@ -13,19 +21,69 @@ 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 (result == 0) {
-    // Disable outputs from tested classes to the standard output
-    std::cout.setstate(std::ios::badbit);
-    result = session.run();
+    const auto& config = session.config();
+    if (config.listReporters() or config.listTags() or config.listTestNamesOnly() or config.listTests()) {
+      if (parallel::rank() == 0) {
+        session.run();
+      }
+    } else {
+      if (parallel::rank() != 0) {
+        // Disable outputs for ranks != 0
+        setenv("GCOV_PREFIX", gcov_prefix.string().c_str(), 1);
+        parallel_output /= output_base_name + std::to_string(parallel::rank());
+
+        Catch::ConfigData data{session.configData()};
+        data.outputFilename = parallel_output.string();
+        session.useConfigData(data);
+      }
+
+      // Disable outputs from tested classes to the standard output
+      std::cout.setstate(std::ios::badbit);
+
+      SynchronizerManager::create();
+      MeshDataManager::create();
+      DiamondDualConnectivityManager::create();
+      DiamondDualMeshManager::create();
+
+      MeshDataBaseForTests::create();
+
+      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';
+          }
+        }
+      }
+
+      result = session.run();
+
+      MeshDataBaseForTests::destroy();
+
+      DiamondDualMeshManager::destroy();
+      DiamondDualConnectivityManager::destroy();
+      MeshDataManager::destroy();
+      SynchronizerManager::destroy();
+    }
   }
 
   Kokkos::finalize();
diff --git a/tests/test_main.cpp b/tests/test_main.cpp
index eba6d05d0ea0cc29b222ec92f4815a3820118c20..7ab5a66fb05301c3e9e41e4140a898bda96cac76 100644
--- a/tests/test_main.cpp
+++ b/tests/test_main.cpp
@@ -3,21 +3,51 @@
 
 #include <Kokkos_Core.hpp>
 
+#include <mesh/DiamondDualConnectivityManager.hpp>
+#include <mesh/DiamondDualMeshManager.hpp>
+#include <mesh/MeshDataManager.hpp>
+#include <mesh/SynchronizerManager.hpp>
+#include <utils/Messenger.hpp>
+
+#include <MeshDataBaseForTests.hpp>
+
 int
 main(int argc, char* argv[])
 {
+  parallel::Messenger::create(argc, argv);
   Kokkos::initialize({4, -1, -1, true});
 
   Catch::Session session;
   int result = session.applyCommandLine(argc, argv);
 
   if (result == 0) {
-    // Disable outputs from tested classes to the standard output
-    std::cout.setstate(std::ios::badbit);
-    result = session.run();
+    const auto& config = session.config();
+    if (config.listReporters() or config.listTags() or config.listTestNamesOnly() or config.listTests()) {
+      result = session.run();
+    } else {
+      // Disable outputs from tested classes to the standard output
+      std::cout.setstate(std::ios::badbit);
+
+      SynchronizerManager::create();
+      MeshDataManager::create();
+      DiamondDualConnectivityManager::create();
+      DiamondDualMeshManager::create();
+
+      MeshDataBaseForTests::create();
+
+      result = session.run();
+
+      MeshDataBaseForTests::destroy();
+
+      DiamondDualMeshManager::destroy();
+      DiamondDualConnectivityManager::destroy();
+      MeshDataManager::destroy();
+      SynchronizerManager::destroy();
+    }
   }
 
   Kokkos::finalize();
+  parallel::Messenger::destroy();
 
   return result;
 }