diff --git a/Tests/MaterialLib/TestThermodynamicForcesView.cpp b/Tests/MaterialLib/TestThermodynamicForcesView.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..8d70dc8751b5eff58aea254c061ec3f5cc03215f
--- /dev/null
+++ b/Tests/MaterialLib/TestThermodynamicForcesView.cpp
@@ -0,0 +1,370 @@
+/**
+ * \file
+ * \copyright
+ * Copyright (c) 2012-2022, OpenGeoSys Community (http://www.opengeosys.org)
+ *            Distributed under a Modified BSD License.
+ *              See accompanying file LICENSE.txt or
+ *              http://www.opengeosys.org/project/license
+ */
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include "MaterialLib/SolidModels/MFront/ThermodynamicForcesView.h"
+#include "MathLib/KelvinVector.h"
+#include "Tests/TestTools.h"
+
+#ifdef OGS_USE_MFRONT
+#include "MaterialLib/SolidModels/MFront/Variable.h"
+#endif
+
+namespace MSM = MaterialLib::Solids::MFront;
+
+// /////////////////////////////////////////////////////////////////////////////
+// Currently (08/2022) there are no vectorial and tensorial variables defined.
+// So we have to provide them here.
+
+#ifdef OGS_USE_MFRONT
+struct Vector : MaterialLib::Solids::MFront::Variable<Vector>
+{
+    constexpr static mgis::behaviour::Variable::Type type =
+        mgis::behaviour::Variable::Type::VECTOR;
+};
+
+struct Tensor : MaterialLib::Solids::MFront::Variable<Tensor>
+{
+    constexpr static mgis::behaviour::Variable::Type type =
+        mgis::behaviour::Variable::Type::TENSOR;
+};
+#endif
+
+// /////////////////////////////////////////////////////////////////////////////
+// The unit tests in this file all have a version that needs MFront and a
+// version that doesn't. For the latter we have to provide the right mock
+// variables, here.
+// The mocking does not only allow tests in environments that do not use MFront,
+// but also shows that OGSMFrontThermodynamicForcesView works entirely without
+// MFront, i.e., can in principle be used in other contexts, too. The necessary
+// adapters have to be like the mock variables in this file.
+
+struct MockStress
+{
+    template <int DisplacementDim>
+    static constexpr std::size_t size()
+    {
+        return rows<DisplacementDim>() * cols<DisplacementDim>();
+    }
+
+    template <int DisplacementDim>
+    static constexpr std::size_t rows()
+    {
+        return MathLib::KelvinVector::kelvin_vector_dimensions(DisplacementDim);
+    }
+
+    template <int DisplacementDim>
+    static constexpr std::size_t cols()
+    {
+        return 1;
+    }
+};
+
+struct MockSaturation
+{
+    template <int DisplacementDim>
+    static constexpr std::size_t size()
+    {
+        return rows<DisplacementDim>() * cols<DisplacementDim>();
+    }
+
+    template <int DisplacementDim>
+    static constexpr std::size_t rows()
+    {
+        return 1;
+    }
+
+    template <int DisplacementDim>
+    static constexpr std::size_t cols()
+    {
+        return 1;
+    }
+};
+
+struct MockVector
+{
+    template <int DisplacementDim>
+    static constexpr std::size_t size()
+    {
+        return rows<DisplacementDim>() * cols<DisplacementDim>();
+    }
+
+    template <int DisplacementDim>
+    static constexpr std::size_t rows()
+    {
+        return DisplacementDim;
+    }
+
+    template <int DisplacementDim>
+    static constexpr std::size_t cols()
+    {
+        return 1;
+    }
+};
+
+struct MockTensor
+{
+    template <int DisplacementDim>
+    static constexpr std::size_t size()
+    {
+        return rows<DisplacementDim>() * cols<DisplacementDim>();
+    }
+
+    template <int DisplacementDim>
+    static constexpr std::size_t rows()
+    {
+        return DisplacementDim;
+    }
+
+    template <int DisplacementDim>
+    static constexpr std::size_t cols()
+    {
+        return DisplacementDim;
+    }
+};
+
+template <typename Stress, typename Saturation>
+static void
+test_MaterialLib_ThermodynamicForcesView_ReadAccess_STensorScalar_3D_impl()
+{
+    using TDynForces = boost::mp11::mp_list<Stress, Saturation>;
+
+    MSM::OGSMFrontThermodynamicForcesData const data{{
+        1, 2, 3, 4, 5, 6,  // stress
+        7                  // saturation
+    }};
+
+    MSM::OGSMFrontThermodynamicForcesView<3, TDynForces> view;
+
+    auto const stress = view.block(Stress{}, data);
+
+    EXPECT_THAT(stress,
+                testing::Pointwise(testing::DoubleEq(), {1, 2, 3, 4, 5, 6}));
+
+    auto const saturation = view.block(Saturation{}, data);
+
+    EXPECT_DOUBLE_EQ(7, saturation);
+}
+
+#ifdef OGS_USE_MFRONT
+TEST(MaterialLib_ThermodynamicForcesView, ReadAccess_STensorScalar_3D)
+{
+    test_MaterialLib_ThermodynamicForcesView_ReadAccess_STensorScalar_3D_impl<
+        MSM::Stress,
+        MSM::Saturation>();
+}
+#endif
+
+TEST(MaterialLib_ThermodynamicForcesView, ReadAccess_STensorScalar_3D_Mock)
+{
+    test_MaterialLib_ThermodynamicForcesView_ReadAccess_STensorScalar_3D_impl<
+        MockStress,
+        MockSaturation>();
+}
+
+template <typename Vector, typename Tensor>
+static void
+test_MaterialLib_ThermodynamicForcesView_ReadAccess_VectorTensor_2D_impl()
+{
+    using TDynForces = boost::mp11::mp_list<Vector, Tensor>;
+
+    MSM::OGSMFrontThermodynamicForcesData const data{{
+        1, 2,       // vector data
+        3, 4, 5, 6  // tensor data
+    }};
+
+    MSM::OGSMFrontThermodynamicForcesView<2, TDynForces> view;
+
+    auto const vector = view.block(Vector{}, data);
+
+    EXPECT_THAT(vector, testing::Pointwise(testing::DoubleEq(), {1, 2}));
+
+    auto const tensor = view.block(Tensor{}, data);
+    auto const tensor_expected = (Eigen::Matrix2d{} << 3, 4, 5, 6).finished();
+
+    ASSERT_PRED_FORMAT2(Tests::EigenIsNear{}, tensor, tensor_expected);
+}
+
+#ifdef OGS_USE_MFRONT
+TEST(MaterialLib_ThermodynamicForcesView, ReadAccess_VectorTensor_2D)
+{
+    test_MaterialLib_ThermodynamicForcesView_ReadAccess_VectorTensor_2D_impl<
+        Vector,
+        Tensor>();
+}
+#endif
+
+TEST(MaterialLib_ThermodynamicForcesView, ReadAccess_VectorTensor_2D_Mock)
+{
+    test_MaterialLib_ThermodynamicForcesView_ReadAccess_VectorTensor_2D_impl<
+        MockVector,
+        MockTensor>();
+}
+
+template <typename Saturation, typename Vector>
+static void
+test_MaterialLib_ThermodynamicForcesView_WriteAccess_ScalarVector_2D_impl()
+{
+    using TDynForces = boost::mp11::mp_list<Saturation, Vector>;
+
+    MSM::OGSMFrontThermodynamicForcesData data{{
+        1,    // saturation
+        2, 3  // vector data
+    }};
+
+    MSM::OGSMFrontThermodynamicForcesView<2, TDynForces> view;
+
+    view.block(Saturation{}, data) = 5;
+    view.block(Vector{}, data)[1] = 7;
+
+    EXPECT_THAT(data.data, testing::Pointwise(testing::DoubleEq(), {5, 2, 7}));
+}
+
+#ifdef OGS_USE_MFRONT
+TEST(MaterialLib_ThermodynamicForcesView, WriteAccess_ScalarVector_2D)
+{
+    test_MaterialLib_ThermodynamicForcesView_WriteAccess_ScalarVector_2D_impl<
+        MSM::Saturation,
+        Vector>();
+}
+#endif
+
+TEST(MaterialLib_ThermodynamicForcesView, WriteAccess_ScalarVector_2D_Mock)
+{
+    test_MaterialLib_ThermodynamicForcesView_WriteAccess_ScalarVector_2D_impl<
+        MockSaturation,
+        MockVector>();
+}
+
+template <typename Tensor, typename STensor>
+static void
+test_MaterialLib_ThermodynamicForcesView_WriteAccess_TensorSTensor_3D_impl()
+{
+    using TDynForces = boost::mp11::mp_list<Tensor, STensor>;
+
+    MSM::OGSMFrontThermodynamicForcesData data{{
+        1, 2, 3, 4, 5, 6, 7, 8, 9,  // tensor data
+        10, 11, 12, 13, 14, 15      // strain
+    }};
+
+    MSM::OGSMFrontThermodynamicForcesView<3, TDynForces> view;
+
+    view.block(STensor{}, data).template segment<3>(1) =
+        Eigen::Vector3d(21, 22, 23);
+    view.block(Tensor{}, data).template block<1, 2>(1, 1) =
+        Eigen::RowVector2d(31, 32);
+
+    EXPECT_THAT(data.data,
+                testing::Pointwise(
+                    testing::DoubleEq(),
+                    {1, 2, 3, 4, 31, 32, 7, 8, 9, 10, 21, 22, 23, 14, 15}));
+}
+
+#ifdef OGS_USE_MFRONT
+TEST(MaterialLib_ThermodynamicForcesView, WriteAccess_TensorSTensor_3D)
+{
+    test_MaterialLib_ThermodynamicForcesView_WriteAccess_TensorSTensor_3D_impl<
+        Tensor,
+        MSM::Strain>();
+}
+#endif
+
+TEST(MaterialLib_ThermodynamicForcesView, WriteAccess_TensorSTensor_3D_Mock)
+{
+    test_MaterialLib_ThermodynamicForcesView_WriteAccess_TensorSTensor_3D_impl<
+        MockTensor,
+        MockStress>();
+}
+
+template <typename Scalar, typename Vector, typename STensor, typename Tensor>
+static void test_MaterialLib_ThermodynamicForcesView_DataSizes_impl()
+{
+    {
+        using TDynForces = boost::mp11::mp_list<Tensor, STensor>;
+
+        static_assert(MSM::OGSMFrontThermodynamicForcesView<3, TDynForces>::
+                          data_size_all_forces == 3 * 3 + 6);
+
+        static_assert(MSM::OGSMFrontThermodynamicForcesView<2, TDynForces>::
+                          data_size_all_forces == 2 * 2 + 4);
+    }
+
+    // same as above, order changed
+    {
+        using TDynForces = boost::mp11::mp_list<STensor, Tensor>;
+
+        static_assert(MSM::OGSMFrontThermodynamicForcesView<3, TDynForces>::
+                          data_size_all_forces == 3 * 3 + 6);
+
+        static_assert(MSM::OGSMFrontThermodynamicForcesView<2, TDynForces>::
+                          data_size_all_forces == 2 * 2 + 4);
+    }
+
+    {
+        using TDynForces = boost::mp11::mp_list<Scalar, Vector>;
+
+        static_assert(MSM::OGSMFrontThermodynamicForcesView<3, TDynForces>::
+                          data_size_all_forces == 1 + 3);
+
+        static_assert(MSM::OGSMFrontThermodynamicForcesView<2, TDynForces>::
+                          data_size_all_forces == 1 + 2);
+    }
+
+    // same as above, order changed
+    {
+        using TDynForces = boost::mp11::mp_list<Vector, Scalar>;
+
+        static_assert(MSM::OGSMFrontThermodynamicForcesView<3, TDynForces>::
+                          data_size_all_forces == 1 + 3);
+
+        static_assert(MSM::OGSMFrontThermodynamicForcesView<2, TDynForces>::
+                          data_size_all_forces == 1 + 2);
+    }
+
+    {
+        using TDynForces = boost::mp11::mp_list<STensor, Scalar>;
+
+        static_assert(MSM::OGSMFrontThermodynamicForcesView<3, TDynForces>::
+                          data_size_all_forces == 6 + 1);
+
+        static_assert(MSM::OGSMFrontThermodynamicForcesView<2, TDynForces>::
+                          data_size_all_forces == 4 + 1);
+    }
+
+    // same as above, order changed
+    {
+        using TDynForces = boost::mp11::mp_list<Scalar, STensor>;
+
+        static_assert(MSM::OGSMFrontThermodynamicForcesView<3, TDynForces>::
+                          data_size_all_forces == 6 + 1);
+
+        static_assert(MSM::OGSMFrontThermodynamicForcesView<2, TDynForces>::
+                          data_size_all_forces == 4 + 1);
+    }
+}
+
+#ifdef OGS_USE_MFRONT
+TEST(MaterialLib_ThermodynamicForcesView, DataSizes)
+{
+    test_MaterialLib_ThermodynamicForcesView_DataSizes_impl<MSM::Saturation,
+                                                            Vector,
+                                                            MSM::Strain,
+                                                            Tensor>();
+}
+#endif
+
+TEST(MaterialLib_ThermodynamicForcesView, DataSizes_Mock)
+{
+    test_MaterialLib_ThermodynamicForcesView_DataSizes_impl<MockSaturation,
+                                                            MockVector,
+                                                            MockStress,
+                                                            MockTensor>();
+}