diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index 14727109ecb2aad2bb356a10efb0d6f9ab47760c..aafe420135bd4ab9679233de041a6d71fc125157 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -183,6 +183,7 @@ if(PUGS_HAS_HDF5)
     test_checkpointing_SubItemArrayPerItemVariant.cpp
     test_checkpointing_SubItemValuePerItemVariant.cpp
     test_checkpointing_Table.cpp
+    test_checkpointing_VariableBCDescriptor.cpp
   )
 endif(PUGS_HAS_HDF5)
 
diff --git a/tests/test_checkpointing_INamedDiscreteData.cpp b/tests/test_checkpointing_INamedDiscreteData.cpp
index d11e29dca2f4d475f8b8a14a8a2e6e3696d961ce..be8bd252c944adb60e7ed9cf6641a086eafb4446 100644
--- a/tests/test_checkpointing_INamedDiscreteData.cpp
+++ b/tests/test_checkpointing_INamedDiscreteData.cpp
@@ -217,8 +217,6 @@ TEST_CASE("checkpointing_INamedDiscreteData", "[utils/checkpointing]")
       using R2x2 = TinyMatrix<2>;
       using R3x3 = TinyMatrix<3>;
 
-      NamedDiscreteFunction ndf;
-
       HighFive::Group checkpoint_group   = file.createGroup("checkpoint");
       HighFive::Group symbol_table_group = checkpoint_group.createGroup("symbol_table");
 
diff --git a/tests/test_checkpointing_VariableBCDescriptor.cpp b/tests/test_checkpointing_VariableBCDescriptor.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..e2d08dda03924c20ea290036e2fc740b32ac1024
--- /dev/null
+++ b/tests/test_checkpointing_VariableBCDescriptor.cpp
@@ -0,0 +1,259 @@
+#include <catch2/catch_test_macros.hpp>
+#include <catch2/matchers/catch_matchers_all.hpp>
+
+#include <utils/Messenger.hpp>
+
+#include <language/utils/DataHandler.hpp>
+#include <language/utils/EmbeddedData.hpp>
+#include <mesh/Mesh.hpp>
+#include <mesh/NamedBoundaryDescriptor.hpp>
+#include <mesh/NumberedBoundaryDescriptor.hpp>
+#include <scheme/DiscreteFunctionVariant.hpp>
+#include <scheme/OutflowBoundaryConditionDescriptor.hpp>
+#include <scheme/SymmetryBoundaryConditionDescriptor.hpp>
+#include <scheme/VariableBCDescriptor.hpp>
+#include <utils/GlobalVariableManager.hpp>
+#include <utils/checkpointing/ReadVariableBCDescriptor.hpp>
+#include <utils/checkpointing/ResumingData.hpp>
+#include <utils/checkpointing/WriteVariableBCDescriptor.hpp>
+
+#include <MeshDataBaseForTests.hpp>
+#include <checkpointing_Connectivity_utilities.hpp>
+#include <checkpointing_Mesh_utilities.hpp>
+
+#include <filesystem>
+
+// clazy:excludeall=non-pod-global-static
+
+namespace test_only
+{
+
+template <typename DataType>
+PUGS_INLINE void
+VariableBCDescriptor_check_is_same_data(const DiscreteFunctionP0<DataType>& reference, const EmbeddedData& e_read_data)
+{
+  auto same_value = [](const auto& a, const auto& b) -> bool {
+    bool same = true;
+    for (size_t i = 0; i < a.size(); ++i) {
+      same &= (a[i] == b[i]);
+    }
+    return parallel::allReduceAnd(same);
+  };
+
+  REQUIRE_NOTHROW(dynamic_cast<const DataHandler<const VariableBCDescriptor>&>(e_read_data.get()));
+
+  const VariableBCDescriptor& var_bc_desc_discrete_data =
+    *dynamic_cast<const DataHandler<const VariableBCDescriptor>&>(e_read_data.get()).data_ptr();
+
+  using DiscreteFunctionT = DiscreteFunctionP0<const DataType>;
+
+  DiscreteFunctionT read_data = var_bc_desc_discrete_data.discreteFunctionVariant()->get<DiscreteFunctionT>();
+
+  REQUIRE(test_only::isSameMesh(read_data.meshVariant(), reference.meshVariant()));
+
+  REQUIRE(same_value(reference.cellValues().arrayView(), read_data.cellValues().arrayView()));
+}
+
+PUGS_INLINE void
+VariableBCDescriptor_check_is_bc_list(
+  const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>& reference_bc_list,
+  const EmbeddedData& e_read_data)
+{
+  REQUIRE_NOTHROW(dynamic_cast<const DataHandler<const VariableBCDescriptor>&>(e_read_data.get()));
+
+  const VariableBCDescriptor& var_bc_desc_discrete_data =
+    *dynamic_cast<const DataHandler<const VariableBCDescriptor>&>(e_read_data.get()).data_ptr();
+
+  auto read_bc_list = var_bc_desc_discrete_data.bcDescriptorList();
+
+  REQUIRE(read_bc_list.size() == reference_bc_list.size());
+  for (size_t i = 0; i < read_bc_list.size(); ++i) {
+    const auto reference_bc = reference_bc_list[i];
+    const auto read_bc      = read_bc_list[i];
+    REQUIRE(read_bc->type() == reference_bc->type());
+    REQUIRE(read_bc->boundaryDescriptor().type() == reference_bc->boundaryDescriptor().type());
+
+    switch (read_bc->boundaryDescriptor().type()) {
+    case IBoundaryDescriptor::Type::named: {
+      const NamedBoundaryDescriptor& ref_named_bc =
+        dynamic_cast<const NamedBoundaryDescriptor&>(reference_bc->boundaryDescriptor());
+      const NamedBoundaryDescriptor& read_named_bc =
+        dynamic_cast<const NamedBoundaryDescriptor&>(read_bc->boundaryDescriptor());
+      REQUIRE(ref_named_bc.name() == read_named_bc.name());
+      break;
+    }
+    case IBoundaryDescriptor::Type::numbered: {
+      const NumberedBoundaryDescriptor& ref_numbered_bc =
+        dynamic_cast<const NumberedBoundaryDescriptor&>(reference_bc->boundaryDescriptor());
+      const NumberedBoundaryDescriptor& read_numbered_bc =
+        dynamic_cast<const NumberedBoundaryDescriptor&>(read_bc->boundaryDescriptor());
+      REQUIRE(ref_numbered_bc.number() == read_numbered_bc.number());
+      break;
+    }
+    }
+  }
+}
+
+}   // namespace test_only
+
+TEST_CASE("checkpointing_VariableBCDescriptor", "[utils/checkpointing]")
+{
+  std::string tmp_dirname;
+  {
+    {
+      if (parallel::rank() == 0) {
+        tmp_dirname = [&]() -> std::string {
+          std::string temp_filename = std::filesystem::temp_directory_path() / "pugs_checkpointing_XXXXXX";
+          return std::string{mkdtemp(&temp_filename[0])};
+        }();
+      }
+      parallel::broadcast(tmp_dirname, 0);
+    }
+    std::filesystem::path path = tmp_dirname;
+    const std::string filename = path / "checkpoint.h5";
+
+    HighFive::FileAccessProps fapl;
+    fapl.add(HighFive::MPIOFileAccess{MPI_COMM_WORLD, MPI_INFO_NULL});
+    fapl.add(HighFive::MPIOCollectiveMetadata{});
+    HighFive::File file = HighFive::File(filename, HighFive::File::Truncate, fapl);
+
+    const size_t initial_connectivity_id = GlobalVariableManager::instance().getConnectivityId();
+    const size_t initial_mesh_id         = GlobalVariableManager::instance().getMeshId();
+
+    SECTION("Mesh")
+    {
+      using R2 = TinyVector<2>;
+      using R3 = TinyVector<3>;
+
+      HighFive::Group checkpoint_group   = file.createGroup("checkpoint");
+      HighFive::Group symbol_table_group = checkpoint_group.createGroup("symbol_table");
+
+      auto mesh_1d = MeshDataBaseForTests::get().unordered1DMesh()->get<Mesh<1>>();
+
+      DiscreteFunctionP0<R2> df_R2_1d{mesh_1d};
+      for (CellId cell_id = 0; cell_id < mesh_1d->numberOfCells(); ++cell_id) {
+        df_R2_1d[cell_id] = R2{std::rand() / (1. * RAND_MAX / mesh_1d->numberOfCells()),
+                               std::rand() / (1. * RAND_MAX / mesh_1d->numberOfCells())};
+      }
+
+      std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>> bc_1d =
+        {std::make_shared<SymmetryBoundaryConditionDescriptor>(std::make_shared<NamedBoundaryDescriptor>("XMIN")),
+         std::make_shared<OutflowBoundaryConditionDescriptor>(std::make_shared<NamedBoundaryDescriptor>("XMAX"))};
+
+      auto mesh_2d = MeshDataBaseForTests::get().hybrid2DMesh()->get<Mesh<2>>();
+
+      DiscreteFunctionP0<R3> df_R3_2d{mesh_2d};
+      for (CellId cell_id = 0; cell_id < mesh_2d->numberOfCells(); ++cell_id) {
+        df_R3_2d[cell_id] = R3{std::rand() / (1. * RAND_MAX / mesh_2d->numberOfCells()),
+                               std::rand() / (1. * RAND_MAX / mesh_2d->numberOfCells()),
+                               std::rand() / (1. * RAND_MAX / mesh_2d->numberOfCells())};
+      }
+
+      std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>> bc_2d =
+        {std::make_shared<SymmetryBoundaryConditionDescriptor>(std::make_shared<NamedBoundaryDescriptor>("XMIN")),
+         std::make_shared<OutflowBoundaryConditionDescriptor>(std::make_shared<NamedBoundaryDescriptor>("XMAX")),
+         std::make_shared<SymmetryBoundaryConditionDescriptor>(std::make_shared<NamedBoundaryDescriptor>("YMIN")),
+         std::make_shared<SymmetryBoundaryConditionDescriptor>(std::make_shared<NamedBoundaryDescriptor>("YMAX"))};
+
+      auto mesh_3d = MeshDataBaseForTests::get().hybrid3DMesh()->get<Mesh<3>>();
+
+      DiscreteFunctionP0<R3> df_R3_3d{mesh_3d};
+      for (CellId cell_id = 0; cell_id < mesh_3d->numberOfCells(); ++cell_id) {
+        df_R3_3d[cell_id] = R3{std::rand() / (1. * RAND_MAX / mesh_3d->numberOfCells()),
+                               std::rand() / (1. * RAND_MAX / mesh_3d->numberOfCells()),
+                               std::rand() / (1. * RAND_MAX / mesh_3d->numberOfCells())};
+      }
+
+      std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>> bc_3d =
+        {std::make_shared<SymmetryBoundaryConditionDescriptor>(std::make_shared<NamedBoundaryDescriptor>("XMIN")),
+         std::make_shared<OutflowBoundaryConditionDescriptor>(std::make_shared<NamedBoundaryDescriptor>("XMAX")),
+         std::make_shared<SymmetryBoundaryConditionDescriptor>(std::make_shared<NamedBoundaryDescriptor>("YMIN")),
+         std::make_shared<SymmetryBoundaryConditionDescriptor>(std::make_shared<NamedBoundaryDescriptor>("YMAX")),
+         std::make_shared<SymmetryBoundaryConditionDescriptor>(std::make_shared<NamedBoundaryDescriptor>("ZMIN")),
+         std::make_shared<SymmetryBoundaryConditionDescriptor>(std::make_shared<NamedBoundaryDescriptor>("ZMAX"))};
+
+      {   // Write
+        using DataHandlerT = DataHandler<const VariableBCDescriptor>;
+
+        auto new_mesh_1d_v              = test_only::duplicateMesh(std::make_shared<MeshVariant>(mesh_1d));
+        auto new_mesh_1d                = new_mesh_1d_v->get<const Mesh<1>>();
+        const auto& new_connectivity_1d = new_mesh_1d->connectivity();
+
+        DiscreteFunctionP0<const R2> df_R2_1d_new{new_mesh_1d_v,
+                                                  CellValue<const R2>{new_connectivity_1d,
+                                                                      df_R2_1d.cellValues().arrayView()}};
+        std::shared_ptr<const VariableBCDescriptor> var_bc_desc_1d =
+          std::make_shared<VariableBCDescriptor>(std::make_shared<const DiscreteFunctionVariant>(df_R2_1d_new), bc_1d);
+
+        checkpointing::writeVariableBCDescriptor("var_bc_desc_1d",
+                                                 EmbeddedData{std::make_shared<DataHandlerT>(var_bc_desc_1d)}, file,
+                                                 checkpoint_group, symbol_table_group);
+
+        auto new_mesh_2d_v              = test_only::duplicateMesh(std::make_shared<MeshVariant>(mesh_2d));
+        auto new_mesh_2d                = new_mesh_2d_v->get<const Mesh<2>>();
+        const auto& new_connectivity_2d = new_mesh_2d->connectivity();
+
+        DiscreteFunctionP0<const R3> df_R3_2d_new{new_mesh_2d_v,
+                                                  CellValue<const R3>{new_connectivity_2d,
+                                                                      df_R3_2d.cellValues().arrayView()}};
+
+        std::shared_ptr<const VariableBCDescriptor> var_bc_desc_2d =
+          std::make_shared<VariableBCDescriptor>(std::make_shared<const DiscreteFunctionVariant>(df_R3_2d_new), bc_2d);
+
+        checkpointing::writeVariableBCDescriptor("var_bc_desc_2d",
+                                                 EmbeddedData{std::make_shared<DataHandlerT>(var_bc_desc_2d)}, file,
+                                                 checkpoint_group, symbol_table_group);
+
+        auto new_mesh_3d_v              = test_only::duplicateMesh(std::make_shared<MeshVariant>(mesh_3d));
+        auto new_mesh_3d                = new_mesh_3d_v->get<const Mesh<3>>();
+        const auto& new_connectivity_3d = new_mesh_3d->connectivity();
+
+        DiscreteFunctionP0<const R3> df_R3_3d_new{new_mesh_3d, CellValue<const R3>{new_connectivity_3d,
+                                                                                   df_R3_3d.cellValues().arrayView()}};
+
+        std::shared_ptr<const VariableBCDescriptor> var_bc_desc_3d =
+          std::make_shared<VariableBCDescriptor>(std::make_shared<const DiscreteFunctionVariant>(df_R3_3d_new), bc_3d);
+
+        checkpointing::writeVariableBCDescriptor("var_bc_desc_3d",
+                                                 EmbeddedData{std::make_shared<DataHandlerT>(var_bc_desc_3d)}, file,
+                                                 checkpoint_group, symbol_table_group);
+
+        HighFive::Group global_variables_group = checkpoint_group.createGroup("singleton/global_variables");
+        global_variables_group.createAttribute("connectivity_id",
+                                               GlobalVariableManager::instance().getConnectivityId());
+        global_variables_group.createAttribute("mesh_id", GlobalVariableManager::instance().getMeshId());
+      }
+
+      // reset to reuse after resuming
+      GlobalVariableManager::instance().setConnectivityId(initial_connectivity_id);
+      GlobalVariableManager::instance().setMeshId(initial_mesh_id);
+
+      file.flush();
+
+      checkpointing::ResumingData::create();
+      checkpointing::ResumingData::instance().readData(checkpoint_group, nullptr);
+
+      GlobalVariableManager::instance().setConnectivityId(initial_connectivity_id);
+      GlobalVariableManager::instance().setMeshId(initial_mesh_id);
+      {   // Read
+        auto e_var_bc_desc_1d = checkpointing::readVariableBCDescriptor("var_bc_desc_1d", symbol_table_group);
+        test_only::VariableBCDescriptor_check_is_bc_list(bc_1d, e_var_bc_desc_1d);
+        test_only::VariableBCDescriptor_check_is_same_data(df_R2_1d, e_var_bc_desc_1d);
+
+        auto e_var_bc_desc_2d = checkpointing::readVariableBCDescriptor("var_bc_desc_2d", symbol_table_group);
+        test_only::VariableBCDescriptor_check_is_bc_list(bc_2d, e_var_bc_desc_2d);
+        test_only::VariableBCDescriptor_check_is_same_data(df_R3_2d, e_var_bc_desc_2d);
+
+        auto e_var_bc_desc_3d = checkpointing::readVariableBCDescriptor("var_bc_desc_3d", symbol_table_group);
+        test_only::VariableBCDescriptor_check_is_bc_list(bc_3d, e_var_bc_desc_3d);
+        test_only::VariableBCDescriptor_check_is_same_data(df_R3_3d, e_var_bc_desc_3d);
+      }
+      checkpointing::ResumingData::destroy();
+    }
+  }
+
+  parallel::barrier();
+  if (parallel::rank() == 0) {
+    std::filesystem::remove_all(std::filesystem::path{tmp_dirname});
+  }
+}