diff --git a/tests/test_determinist.cpp b/tests/test_determinist.cpp
index 7b89ddb1bdd068c0bcf9df82424080fc27eb7c57..8f7df0817539f442d64fa4d298f456e4d5834eef 100644
--- a/tests/test_determinist.cpp
+++ b/tests/test_determinist.cpp
@@ -1,5 +1,6 @@
 #include <fstream>
 #include <vector>
+#include <tuple>
 #include <array>
 #include <gtest/gtest.h>
 #include <xtensor/xtensor.hpp>
@@ -11,19 +12,48 @@
 
 using namespace xt::placeholders;  // required for `_` to work
 
-TEST(DeterministTests, TestMetrics2D)
+template <std::size_t N>
+std::tuple<xt::xtensor<double, N>, xt::xtensor<double, N>> load_data()
 {
     // read in data
-    std::ifstream ifs;
-    ifs.open("./data/q_obs.csv");
-    xt::xtensor<double, 2> observed =xt::load_csv<int>(ifs);
-    ifs.close();
+    if (N == 2)
+    {
+        std::ifstream ifs;
+        ifs.open("./data/q_obs.csv");
+        xt::xtensor<double, 2> observed = xt::load_csv<int>(ifs);
+        ifs.close();
+
+        ifs.open("./data/q_prd.csv");
+        xt::xtensor<double, 2> predicted = xt::view(
+                xt::load_csv<double>(ifs), xt::range(0, 5), xt::all()
+        );
+        ifs.close();
+
+        return std::make_tuple(observed, predicted);
+    }
+    else
+    {
+        std::ifstream ifs;
+        ifs.open("./data/q_obs.csv");
+        xt::xtensor<double, 1> observed = xt::squeeze(xt::load_csv<int>(ifs));
+        ifs.close();
+
+        ifs.open("./data/q_prd.csv");
+        xt::xtensor<double, 1> predicted = xt::view(
+                xt::load_csv<double>(ifs), 0, xt::all()
+        );
+        ifs.close();
+
+        return std::make_tuple(observed, predicted);
+    }
+}
 
-    ifs.open("./data/q_prd.csv");
-    xt::xtensor<double, 2> predicted = xt::view(
-            xt::load_csv<double>(ifs), xt::range(0, 5), xt::all()
-    );
-    ifs.close();
+TEST(DeterministTests, TestMetrics2D)
+{
+    // read in data
+    xt::xtensor<double, 2> observed;
+    xt::xtensor<double, 2> predicted;
+    std::tie(observed, predicted) = load_data<2>();
 
     // compute scores (with 2D tensors)
     std::vector<xt::xarray<double>> metrics =
@@ -68,16 +98,9 @@ TEST(DeterministTests, TestMetrics2D)
 TEST(DeterministTests, TestMetrics1D)
 {
     // read in data
-    std::ifstream ifs;
-    ifs.open("./data/q_obs.csv");
-    xt::xtensor<double, 1> observed = xt::squeeze(xt::load_csv<int>(ifs));
-    ifs.close();
-
-    ifs.open("./data/q_prd.csv");
-    xt::xtensor<double, 1> predicted = xt::row(
-            xt::view(xt::load_csv<double>(ifs), xt::range(0, 5), xt::all()), 0
-    );
-    ifs.close();
+    xt::xtensor<double, 1> observed;
+    xt::xtensor<double, 1> predicted;
+    std::tie(observed, predicted) = load_data<1>();
 
     // compute scores (with 1D tensors)
     std::vector<xt::xarray<double>> metrics =
@@ -102,16 +125,9 @@ TEST(DeterministTests, TestMetrics1D)
 TEST(DeterministTests, TestTransform)
 {
     // read in data
-    std::ifstream ifs;
-    ifs.open("./data/q_obs.csv");
-    xt::xtensor<double, 2> observed =xt::load_csv<int>(ifs);
-    ifs.close();
-
-    ifs.open("./data/q_prd.csv");
-    xt::xtensor<double, 2> predicted = xt::view(
-            xt::load_csv<double>(ifs), xt::range(0, 5), xt::all()
-    );
-    ifs.close();
+    xt::xtensor<double, 2> observed;
+    xt::xtensor<double, 2> predicted;
+    std::tie(observed, predicted) = load_data<2>();
 
     // compute and check results on square-rooted streamflow series
     std::vector<xt::xarray<double>> metrics =
@@ -163,14 +179,9 @@ TEST(DeterministTests, TestTransform)
 TEST(DeterministTests, TestMasks)
 {
     // read in data
-    std::ifstream ifs;
-    ifs.open("./data/q_obs.csv");
-    xt::xtensor<double, 2> observed = xt::load_csv<int>(ifs);
-    ifs.close();
-
-    ifs.open("./data/q_prd.csv");
-    xt::xtensor<double, 2> predicted = xt::load_csv<double>(ifs);
-    ifs.close();
+    xt::xtensor<double, 2> observed;
+    xt::xtensor<double, 2> predicted;
+    std::tie(observed, predicted) = load_data<2>();
 
     // generate temporal subset by dropping 20 first time steps
     xt::xtensor<double, 2> masks =
@@ -205,14 +216,9 @@ TEST(DeterministTests, TestMaskingConditions)
             {"RMSE", "NSE", "KGE", "KGEPRIME"};
 
     // read in data
-    std::ifstream ifs;
-    ifs.open("./data/q_obs.csv");
-    xt::xtensor<double, 2> observed = xt::load_csv<int>(ifs);
-    ifs.close();
-
-    ifs.open("./data/q_prd.csv");
-    xt::xtensor<double, 2> predicted = xt::load_csv<double>(ifs);
-    ifs.close();
+    xt::xtensor<double, 2> observed;
+    xt::xtensor<double, 2> predicted;
+    std::tie(observed, predicted) = load_data<2>();
 
     // generate dummy empty masks required to access next optional argument
     xt::xtensor<bool, 2> masks;
@@ -319,16 +325,9 @@ TEST(DeterministTests, TestMissingData)
             {"RMSE", "NSE", "KGE", "KGEPRIME"};
 
     // read in data
-    std::ifstream ifs;
-    ifs.open("./data/q_obs.csv");
-    xt::xtensor<double, 2> observed = xt::load_csv<int>(ifs);
-    ifs.close();
-
-    ifs.open("./data/q_prd.csv");
-    xt::xtensor<double, 2> predicted = xt::view(
-            xt::load_csv<double>(ifs), xt::range(0, 5), xt::all()
-    );
-    ifs.close();
+    xt::xtensor<double, 2> observed;
+    xt::xtensor<double, 2> predicted;
+    std::tie(observed, predicted) = load_data<2>();
 
     // add some missing observations artificially by assigning NaN values
     xt::view(observed, xt::all(), xt::range(0, 20)) = NAN;
diff --git a/tests/test_probabilist.cpp b/tests/test_probabilist.cpp
index 2bfc446f2255c4475bc46215ff4dd0f0e83dba61..fa9a8563f49cac44930d3b31c947b3315f0d57a0 100644
--- a/tests/test_probabilist.cpp
+++ b/tests/test_probabilist.cpp
@@ -1,5 +1,6 @@
 #include <fstream>
 #include <vector>
+#include <tuple>
 #include <array>
 #include <gtest/gtest.h>
 #include <xtensor/xtensor.hpp>
@@ -10,7 +11,8 @@
 
 using namespace xt::placeholders;  // required for `_` to work
 
-TEST(ProbabilistTests, TestBrier)
+
+std::tuple<xt::xtensor<double, 1>, xt::xtensor<double, 2>> load_data()
 {
     // read in data
     std::ifstream ifs;
@@ -22,6 +24,16 @@ TEST(ProbabilistTests, TestBrier)
     xt::xtensor<double, 2> predicted = xt::load_csv<double>(ifs);
     ifs.close();
 
+    return std::make_tuple(observed, predicted);
+}
+
+TEST(ProbabilistTests, TestBrier)
+{
+    // read in data
+    xt::xtensor<double, 1> observed;
+    xt::xtensor<double, 2> predicted;
+    std::tie(observed, predicted) = load_data();
+
     // compute scores
     xt::xtensor<double, 2> thresholds = {{690, 534, 445, NAN}};
 
@@ -78,14 +90,9 @@ TEST(ProbabilistTests, TestBrier)
 TEST(ProbabilistTests, TestQuantiles)
 {
     // read in data
-    std::ifstream ifs;
-    ifs.open("./data/q_obs.csv");
-    xt::xtensor<double, 1> observed = xt::squeeze(xt::load_csv<int>(ifs));
-    ifs.close();
-
-    ifs.open("./data/q_prd.csv");
-    xt::xtensor<double, 2> predicted = xt::load_csv<double>(ifs);
-    ifs.close();
+    xt::xtensor<double, 1> observed;
+    xt::xtensor<double, 2> predicted;
+    std::tie(observed, predicted) = load_data();
 
     // compute scores
     std::vector<xt::xarray<double>> metrics =
@@ -122,14 +129,9 @@ TEST(ProbabilistTests, TestQuantiles)
 TEST(ProbabilistTests, TestMasks)
 {
     // read in data
-    std::ifstream ifs;
-    ifs.open("./data/q_obs.csv");
-    xt::xtensor<double, 1> observed = xt::squeeze(xt::load_csv<int>(ifs));
-    ifs.close();
-
-    ifs.open("./data/q_prd.csv");
-    xt::xtensor<double, 2> predicted = xt::load_csv<double>(ifs);
-    ifs.close();
+    xt::xtensor<double, 1> observed;
+    xt::xtensor<double, 2> predicted;
+    std::tie(observed, predicted) = load_data();
 
     // generate temporal subset by dropping 20 first time steps
     xt::xtensor<double, 4> masks =
@@ -180,15 +182,12 @@ TEST(ProbabilistTests, TestMaskingConditions)
             {"BS", "BSS", "BS_CRD", "BS_LBD", "QS", "CRPS"};
 
     // read in data
-    // read in data
-    std::ifstream ifs;
-    ifs.open("./data/q_obs.csv");
-    xt::xtensor<double, 2> observed = xt::load_csv<int>(ifs);
-    ifs.close();
+    xt::xtensor<double, 1> observed_;
+    xt::xtensor<double, 2> predicted;
+    std::tie(observed_, predicted) = load_data();
 
-    ifs.open("./data/q_prd.csv");
-    xt::xtensor<double, 2> predicted = xt::load_csv<double>(ifs);
-    ifs.close();
+    // turn observed into 2D view (to simplify syntax later on)
+    auto observed = xt::view(observed_, xt::newaxis(), xt::all());
 
     // generate dummy empty masks required to access next optional argument
     xt::xtensor<bool, 4> masks;