diff --git a/src/language/SymbolTable.hpp b/src/language/SymbolTable.hpp
index e86bd1e45c5eaedce0cba80a6581bd4f25688b0a..420a8fd8cf3ad987ebe308b4a114390f7d83ee8c 100644
--- a/src/language/SymbolTable.hpp
+++ b/src/language/SymbolTable.hpp
@@ -2,6 +2,7 @@
 #define SYMBOL_TABLE_HPP
 
 #include <ASTNodeDataType.hpp>
+#include <ASTNodeDataVariant.hpp>
 
 class SymbolTable
 {
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index 71d2efbfb1351ea7f966c9853e28ac9281a70b20..bd30945f2fab3f09362b27e25f74cc830496b9a9 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -9,6 +9,7 @@ add_executable (unit_tests
   test_ItemType.cpp
   test_PugsAssert.cpp
   test_RevisionInfo.cpp
+  test_SymbolTable.cpp
   test_TinyMatrix.cpp
   test_TinyVector.cpp
   )
diff --git a/tests/test_SymbolTable.cpp b/tests/test_SymbolTable.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..9d7d02bde1e9f2b3c29ce479694acaa82c8a3489
--- /dev/null
+++ b/tests/test_SymbolTable.cpp
@@ -0,0 +1,106 @@
+#include <catch2/catch.hpp>
+
+#include <SymbolTable.hpp>
+#include <sstream>
+
+TEST_CASE("SymbolTable", "[language]")
+{
+  SECTION("Simple Symbol Table")
+  {
+    std::shared_ptr root_st = std::make_shared<SymbolTable>();
+
+    auto [i_symbol_a, created_a] = root_st->add("a");
+    REQUIRE(created_a);
+
+    // Check that one cannot build another "a" in this table
+    REQUIRE(not root_st->add("a").second);
+
+    auto [i_search_a, found_a] = root_st->find("a");
+    REQUIRE(found_a);
+    REQUIRE(i_search_a == i_symbol_a);
+
+    SECTION("Output uninitialized")
+    {
+      std::stringstream st_output;
+      st_output << '\n' << *root_st;
+
+      REQUIRE(st_output.str() == R"(
+-- Symbol table state -- parent : 0
+ a: --
+------------------------
+)");
+    }
+
+    SECTION("Attributes")
+    {
+      // undefined data
+      auto& attributes_a = i_search_a->second;
+      REQUIRE(attributes_a.dataType() == ASTNodeDataType::undefined_t);
+      REQUIRE(not attributes_a.isInitialized());
+
+      REQUIRE(std::holds_alternative<std::monostate>(attributes_a.value()));
+
+      {
+        std::stringstream value_output;
+        value_output << attributes_a;
+        REQUIRE(value_output.str() == "--");
+      }
+
+      // defining data
+      attributes_a.setIsInitialized();
+      REQUIRE(attributes_a.isInitialized());
+
+      attributes_a.setDataType(ASTNodeDataType::double_t);
+      REQUIRE(attributes_a.dataType() == ASTNodeDataType::double_t);
+
+      attributes_a.value() = 2.3;
+
+      REQUIRE(std::holds_alternative<double>(attributes_a.value()));
+
+      {
+        std::stringstream value_output;
+        value_output << attributes_a;
+        REQUIRE(value_output.str() == "2.3");
+      }
+
+      SECTION("Output initialized")
+      {
+        std::stringstream st_output;
+        st_output << '\n' << *root_st;
+
+        REQUIRE(st_output.str() == R"(
+-- Symbol table state -- parent : 0
+ a: 2.3
+------------------------
+)");
+      }
+    }
+  }
+
+  SECTION("Hierarchy Symbol Table")
+  {
+    std::shared_ptr root_st                = std::make_shared<SymbolTable>();
+    auto [i_root_symbol_a, created_root_a] = root_st->add("a");
+    REQUIRE(created_root_a);
+
+    std::shared_ptr nested_st = std::make_shared<SymbolTable>(root_st);
+
+    auto [i_search_a, found_a] = nested_st->find("a");
+    REQUIRE(found_a);
+    // symbol "a" is the one defined in root_st
+    REQUIRE(i_root_symbol_a == i_search_a);
+
+    auto [i_nested_symbol_a, created_nested_a] = nested_st->add("a");
+    REQUIRE(created_nested_a);
+
+    auto [i_search_nested_a, found_nested_a] = nested_st->find("a");
+
+    REQUIRE(found_nested_a);
+    // found the symbol created in nested symbol table
+    REQUIRE(&(i_search_nested_a->second) != &(i_root_symbol_a->second));
+    REQUIRE(&(i_search_nested_a->second) == &(i_nested_symbol_a->second));
+
+    auto [i_search_b, found_b] = nested_st->find("b");
+    REQUIRE(not found_b);   // "b" is not defined in any symbol table
+  }
+}