diff --git a/test/common/CMakeLists.txt b/test/common/CMakeLists.txt
index e680af29b0ad4c5e129e32b3d65f14ecdc98d98e..fedef0b7c56895f538cd7f52c0c521c9fd8af42a 100644
--- a/test/common/CMakeLists.txt
+++ b/test/common/CMakeLists.txt
@@ -1,6 +1,7 @@
 # SPDX-FileCopyrightInfo: Copyright © DuMux Project contributors, see AUTHORS.md in root folder
 # SPDX-License-Identifier: GPL-3.0-or-later
 
+add_subdirectory(functionfromstringexpression)
 add_subdirectory(functions)
 add_subdirectory(integrate)
 add_subdirectory(math)
diff --git a/test/common/functionfromstringexpression/CMakeLists.txt b/test/common/functionfromstringexpression/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..ef7fc2e321d8ac26a893b461daa85a91b6165e78
--- /dev/null
+++ b/test/common/functionfromstringexpression/CMakeLists.txt
@@ -0,0 +1,5 @@
+# SPDX-FileCopyrightInfo: Copyright © DuMux Project contributors, see AUTHORS.md in root folder
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+dumux_add_test(SOURCES test_functionfromstringexpression.cc
+               LABELS unit)
diff --git a/test/common/functionfromstringexpression/test_functionfromstringexpression.cc b/test/common/functionfromstringexpression/test_functionfromstringexpression.cc
new file mode 100644
index 0000000000000000000000000000000000000000..e97865af37d9f3942047517ade83793e3067ad39
--- /dev/null
+++ b/test/common/functionfromstringexpression/test_functionfromstringexpression.cc
@@ -0,0 +1,79 @@
+//
+// SPDX-FileCopyrightInfo: Copyright © DuMux Project contributors, see AUTHORS.md in root folder
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+#include <config.h>
+
+#include <string>
+
+#include <dune/common/exceptions.hh>
+#include <dune/common/float_cmp.hh>
+
+#include <dumux/common/math.hh>
+#include <dumux/common/functionfromstringexpression.hh>
+
+int main(int argc, char* argv[])
+{
+    using namespace Dumux;
+
+    constexpr std::size_t numSamples = 100;
+    const auto values0 = linspace(-M_PI, M_PI, numSamples);
+    const auto values1 = linspace(-1.0, 1.0, numSamples);
+
+    const auto test1Arg = [&](auto&& f1, auto&& f2)
+    {
+        for (int i = 0; i < numSamples; ++i)
+            if (Dune::FloatCmp::ne<double>(f1(values0[i]), f2(values0[i])))
+                DUNE_THROW(Dune::Exception, "Results do not match: "
+                    << f1(values0[i]) << " " << f2(values0[i]));
+
+    };
+
+    const auto test2Args = [&](auto&& f1, auto&& f2)
+    {
+        for (int i = 0; i < numSamples; ++i)
+            if (Dune::FloatCmp::ne<double>(f1(values0[i], values1[i]), f2(values0[i], values1[i])))
+                DUNE_THROW(Dune::Exception, "Results do not match: "
+                    << f1(values0[i], values1[i]) << " " << f2(values0[i], values1[i]));
+    };
+
+    // function with one argument
+    {
+        const std::string funcStr = "sin(t)";
+        FunctionFromStringExpression<1> func(funcStr, "t");
+        test1Arg(func, [](auto t) { return std::sin(t); });
+    }
+
+    // function with two arguments
+    {
+        const std::string funcStr = "x*sin(t)";
+        FunctionFromStringExpression<2> func(funcStr, "xt");
+        test2Args(func, [](auto x, auto t) { return x*std::sin(t); });
+    }
+
+    // function with longer variable names
+    {
+        const std::string funcStr = "foo*sin(bar)";
+        FunctionFromStringExpression<2> func(funcStr, std::array<std::string, 2>{{"foo", "bar"}});
+        test2Args(func, [](auto foo, auto bar) { return foo*std::sin(bar); });
+    }
+
+    // function with expression-local variables
+    {
+        const std::string funcStr = "var b:=2; x*sin(t) + b + 24;";
+        FunctionFromStringExpression<2> func(funcStr, "xt");
+        test2Args(func, [](auto x, auto t) { double b = 2; return x*std::sin(t) + b + 24; });
+    }
+
+    // faulty expression that has to throw an exception
+    {
+        const std::string funcStr = "foo*sin(bar)*bla";
+        bool error = false;
+        try { FunctionFromStringExpression<2> func(funcStr, std::array<std::string, 2>{{"foo" "bar"}}); }
+        catch (Dune::IOError& e) { error = true; }
+        if (!error)
+            DUNE_THROW(Dune::Exception, "Faulty expression did not throw: " << funcStr);
+    }
+
+    return 0;
+}