diff --git a/src/language/ASTNodeFunctionExpressionBuilder.cpp b/src/language/ASTNodeFunctionExpressionBuilder.cpp
index 4a7e095284b79227b344c4e8593596505ae5bca6..90c7559a9182a7be4c72541a9c4d098561c2ce25 100644
--- a/src/language/ASTNodeFunctionExpressionBuilder.cpp
+++ b/src/language/ASTNodeFunctionExpressionBuilder.cpp
@@ -80,8 +80,8 @@ ASTNodeFunctionExpressionBuilder::_buildArgumentProcessors(FunctionDescriptor& f
                                                            ASTNode& node,
                                                            FunctionProcessor& function_processor)
 {
-  ASTNode& definition_node     = function_descriptor.definitionNode();
-  ASTNode& parameter_variables = *definition_node.children[0];
+  const ASTNode& definition_node = function_descriptor.definitionNode();
+  ASTNode& parameter_variables   = *definition_node.children[0];
 
   ASTNode& argument_nodes = *node.children[1];
 
diff --git a/src/language/FunctionTable.hpp b/src/language/FunctionTable.hpp
index 682a87c4c9e5d4c9b097e88db2cb6ce37876ae00..451e95aeb11ab0fa0958f505578dcf7e99e00770 100644
--- a/src/language/FunctionTable.hpp
+++ b/src/language/FunctionTable.hpp
@@ -19,16 +19,16 @@ class FunctionDescriptor
 
  public:
   auto&
-  domainMappingNode()
+  domainMappingNode() const
   {
     Assert(m_domain_mapping_node, "undefined domain mapping node");
     return *m_domain_mapping_node;
   }
 
   auto&
-  definitionNode()
+  definitionNode() const
   {
-    Assert(m_domain_mapping_node, "undefined expression node");
+    Assert(m_definition_node, "undefined expression node");
     return *m_definition_node;
   }
 
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index 8d210ede78620ee09f9294c8a9145de0e36b8745..7f8cd3eb99933803d286251158dcc810f0b0c194 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -47,6 +47,7 @@ add_executable (unit_tests
   test_ExecUntilBreakOrContinue.cpp
   test_FakeProcessor.cpp
   test_ForProcessor.cpp
+  test_FunctionTable.cpp
   test_IfProcessor.cpp
   test_IncDecExpressionProcessor.cpp
   test_INodeProcessor.cpp
diff --git a/tests/test_FunctionTable.cpp b/tests/test_FunctionTable.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..7a28f1a56571a5a609726f103e968ad97d12a441
--- /dev/null
+++ b/tests/test_FunctionTable.cpp
@@ -0,0 +1,92 @@
+#include <catch2/catch.hpp>
+
+#include <FunctionTable.hpp>
+
+TEST_CASE("FunctionTable", "[language]")
+{
+  rang::setControlMode(rang::control::Off);
+
+  SECTION("FunctionDescriptor")
+  {
+    std::unique_ptr domain_mapping_node = std::make_unique<ASTNode>();
+    domain_mapping_node->m_data_type    = ASTNodeDataType::unsigned_int_t;
+
+    std::unique_ptr definition_node = std::make_unique<ASTNode>();
+    definition_node->m_data_type    = ASTNodeDataType::double_t;
+    FunctionDescriptor f{std::move(domain_mapping_node), std::move(definition_node)};
+
+    REQUIRE(f.domainMappingNode().m_data_type == ASTNodeDataType::unsigned_int_t);
+    REQUIRE(f.definitionNode().m_data_type == ASTNodeDataType::double_t);
+
+    REQUIRE(domain_mapping_node == nullptr);
+    REQUIRE(definition_node == nullptr);
+  }
+
+#ifndef NDEBUG
+  SECTION("uninitialized FunctionDescriptor")
+  {
+    std::unique_ptr domain_mapping_node = std::make_unique<ASTNode>();
+    domain_mapping_node->m_data_type    = ASTNodeDataType::unsigned_int_t;
+
+    std::unique_ptr definition_node = std::make_unique<ASTNode>();
+    definition_node->m_data_type    = ASTNodeDataType::double_t;
+
+    SECTION("nothing initialized")
+    {
+      FunctionDescriptor f{nullptr, nullptr};
+      REQUIRE_THROWS_AS(f.domainMappingNode(), AssertError);
+      REQUIRE_THROWS_AS(f.definitionNode(), AssertError);
+    }
+
+    SECTION("domain mapping uninitialized")
+    {
+      FunctionDescriptor f{nullptr, std::move(definition_node)};
+      REQUIRE_THROWS_AS(f.domainMappingNode(), AssertError);
+      REQUIRE(f.definitionNode().m_data_type == ASTNodeDataType::double_t);
+      REQUIRE(definition_node == nullptr);
+    }
+
+    SECTION("definition node uninitialized")
+    {
+      FunctionDescriptor f{std::move(domain_mapping_node), nullptr};
+      REQUIRE_THROWS_AS(f.definitionNode(), AssertError);
+      REQUIRE(f.domainMappingNode().m_data_type == ASTNodeDataType::unsigned_int_t);
+      REQUIRE(domain_mapping_node == nullptr);
+    }
+  }
+#endif   // NDEBUG
+
+  FunctionTable table;
+
+  REQUIRE(table.size() == 0);
+
+  std::unique_ptr domain_mapping_node = std::make_unique<ASTNode>();
+  domain_mapping_node->m_data_type    = ASTNodeDataType::unsigned_int_t;
+
+  std::unique_ptr definition_node = std::make_unique<ASTNode>();
+  definition_node->m_data_type    = ASTNodeDataType::double_t;
+
+  size_t function_id = table.add(FunctionDescriptor{std::move(domain_mapping_node), std::move(definition_node)});
+
+  REQUIRE(domain_mapping_node == nullptr);
+  REQUIRE(definition_node == nullptr);
+
+  REQUIRE(function_id == 0);
+  REQUIRE(table.size() == 1);
+
+  const auto& const_table = table;
+  REQUIRE(const_table.size() == 1);
+
+  auto& f = table[function_id];
+  REQUIRE(f.domainMappingNode().m_data_type == ASTNodeDataType::unsigned_int_t);
+  REQUIRE(f.definitionNode().m_data_type == ASTNodeDataType::double_t);
+
+  const auto& const_f = const_table[function_id];
+  REQUIRE(const_f.domainMappingNode().m_data_type == ASTNodeDataType::unsigned_int_t);
+  REQUIRE(const_f.definitionNode().m_data_type == ASTNodeDataType::double_t);
+
+#ifndef NDEBUG
+  REQUIRE_THROWS_AS(table[table.size()], AssertError);
+  REQUIRE_THROWS_AS(const_table[const_table.size()], AssertError);
+#endif   // NDEBUG
+}