diff --git a/CHANGELOG.md b/CHANGELOG.md
index 4d484dde2..f538bf5e7 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,6 +4,8 @@
 
 ### Major Features and Improvements
 
+* CASE construct (without aggregations)
+
 ### Bug Fixes and Other Changes
 
 * Keywords appearing in header (named expressions) keep original case.
diff --git a/docs/user_technical/open-cypher.md b/docs/user_technical/open-cypher.md
index adc33bfcf..a27c936c7 100644
--- a/docs/user_technical/open-cypher.md
+++ b/docs/user_technical/open-cypher.md
@@ -478,6 +478,10 @@ a dictionary and convert them to strings before running a query:
 To use parameters with some other driver please consult appropriate
 documentation.
 
+#### CASE
+
+TODO
+
 ### Differences
 
 Although we try to implement openCypher query language as closely to the
diff --git a/src/query/frontend/ast/ast.hpp b/src/query/frontend/ast/ast.hpp
index c81b30578..b507061c4 100644
--- a/src/query/frontend/ast/ast.hpp
+++ b/src/query/frontend/ast/ast.hpp
@@ -447,6 +447,45 @@ class ListSlicingOperator : public Expression {
         upper_bound_(upper_bound) {}
 };
 
+class IfOperator : public Expression {
+  friend class AstTreeStorage;
+
+ public:
+  DEFVISITABLE(TreeVisitor<TypedValue>);
+  bool Accept(HierarchicalTreeVisitor &visitor) override {
+    if (visitor.PreVisit(*this)) {
+      condition_->Accept(visitor) && then_expression_->Accept(visitor) &&
+          else_expression_->Accept(visitor);
+    }
+    return visitor.PostVisit(*this);
+  }
+
+  IfOperator *Clone(AstTreeStorage &storage) const override {
+    return storage.Create<IfOperator>(condition_->Clone(storage),
+                                      then_expression_->Clone(storage),
+                                      else_expression_->Clone(storage));
+  }
+
+  // None of the expressions should be nullptrs. If there is no else_expression
+  // you probably want to make it NULL PrimitiveLiteral.
+  Expression *condition_;
+  Expression *then_expression_;
+  Expression *else_expression_;
+
+ protected:
+  IfOperator(int uid, Expression *condition, Expression *then_expression,
+             Expression *else_expression)
+      : Expression(uid),
+        condition_(condition),
+        then_expression_(then_expression),
+        else_expression_(else_expression) {
+    debug_assert(
+        condition_ != nullptr && then_expression_ != nullptr &&
+            else_expression_ != nullptr,
+        "clause_, then_expression_ and else_expression_ can't be nullptr");
+  }
+};
+
 class NotOperator : public UnaryOperator {
   friend class AstTreeStorage;
 
diff --git a/src/query/frontend/ast/ast_visitor.hpp b/src/query/frontend/ast/ast_visitor.hpp
index 42641b793..e3063ca53 100644
--- a/src/query/frontend/ast/ast_visitor.hpp
+++ b/src/query/frontend/ast/ast_visitor.hpp
@@ -47,6 +47,7 @@ class GreaterEqualOperator;
 class InListOperator;
 class ListIndexingOperator;
 class ListSlicingOperator;
+class IfOperator;
 class Delete;
 class Where;
 class SetProperty;
@@ -64,11 +65,11 @@ using TreeCompositeVisitor = ::utils::CompositeVisitor<
     MultiplicationOperator, DivisionOperator, ModOperator, NotEqualOperator,
     EqualOperator, LessOperator, GreaterOperator, LessEqualOperator,
     GreaterEqualOperator, InListOperator, ListIndexingOperator,
-    ListSlicingOperator, UnaryPlusOperator, UnaryMinusOperator, IsNullOperator,
-    ListLiteral, MapLiteral, PropertyLookup, LabelsTest, EdgeTypeTest,
-    Aggregation, Function, All, Create, Match, Return, With, Pattern, NodeAtom,
-    EdgeAtom, BreadthFirstAtom, Delete, Where, SetProperty, SetProperties,
-    SetLabels, RemoveProperty, RemoveLabels, Merge, Unwind>;
+    ListSlicingOperator, IfOperator, UnaryPlusOperator, UnaryMinusOperator,
+    IsNullOperator, ListLiteral, MapLiteral, PropertyLookup, LabelsTest,
+    EdgeTypeTest, Aggregation, Function, All, Create, Match, Return, With,
+    Pattern, NodeAtom, EdgeAtom, BreadthFirstAtom, Delete, Where, SetProperty,
+    SetProperties, SetLabels, RemoveProperty, RemoveLabels, Merge, Unwind>;
 
 using TreeLeafVisitor =
     ::utils::LeafVisitor<Identifier, PrimitiveLiteral, CreateIndex>;
@@ -89,11 +90,11 @@ using TreeVisitor = ::utils::Visitor<
     MultiplicationOperator, DivisionOperator, ModOperator, NotEqualOperator,
     EqualOperator, LessOperator, GreaterOperator, LessEqualOperator,
     GreaterEqualOperator, InListOperator, ListIndexingOperator,
-    ListSlicingOperator, UnaryPlusOperator, UnaryMinusOperator, IsNullOperator,
-    ListLiteral, MapLiteral, PropertyLookup, LabelsTest, EdgeTypeTest,
-    Aggregation, Function, All, Create, Match, Return, With, Pattern, NodeAtom,
-    EdgeAtom, BreadthFirstAtom, Delete, Where, SetProperty, SetProperties,
-    SetLabels, RemoveProperty, RemoveLabels, Merge, Unwind, Identifier,
-    PrimitiveLiteral, CreateIndex>;
+    ListSlicingOperator, IfOperator, UnaryPlusOperator, UnaryMinusOperator,
+    IsNullOperator, ListLiteral, MapLiteral, PropertyLookup, LabelsTest,
+    EdgeTypeTest, Aggregation, Function, All, Create, Match, Return, With,
+    Pattern, NodeAtom, EdgeAtom, BreadthFirstAtom, Delete, Where, SetProperty,
+    SetProperties, SetLabels, RemoveProperty, RemoveLabels, Merge, Unwind,
+    Identifier, PrimitiveLiteral, CreateIndex>;
 
 }  // namespace query
diff --git a/src/query/frontend/ast/cypher_main_visitor.cpp b/src/query/frontend/ast/cypher_main_visitor.cpp
index 2439290a0..05e2f81f4 100644
--- a/src/query/frontend/ast/cypher_main_visitor.cpp
+++ b/src/query/frontend/ast/cypher_main_visitor.cpp
@@ -797,6 +797,8 @@ antlrcpp::Any CypherMainVisitor::visitAtom(CypherParser::AtomContext *ctx) {
     Where *where = ctx->filterExpression()->where()->accept(this);
     return static_cast<Expression *>(
         storage_.Create<All>(ident, list_expr, where));
+  } else if (ctx->caseExpression()) {
+    return static_cast<Expression *>(ctx->caseExpression()->accept(this));
   }
   // TODO: Implement this. We don't support comprehensions, filtering... at
   // the moment.
@@ -1032,6 +1034,36 @@ antlrcpp::Any CypherMainVisitor::visitPropertyExpression(
   return dynamic_cast<PropertyLookup *>(expression);
 }
 
+antlrcpp::Any CypherMainVisitor::visitCaseExpression(
+    CypherParser::CaseExpressionContext *ctx) {
+  Expression *test_expression =
+      ctx->test ? ctx->test->accept(this).as<Expression *>() : nullptr;
+  auto alternatives = ctx->caseAlternatives();
+  // Reverse alternatives so that tree of IfOperators can be built bottom-up.
+  std::reverse(alternatives.begin(), alternatives.end());
+  Expression *else_expression =
+      ctx->else_expression
+          ? ctx->else_expression->accept(this).as<Expression *>()
+          : storage_.Create<PrimitiveLiteral>(TypedValue::Null);
+  for (auto *alternative : alternatives) {
+    Expression *condition =
+        test_expression
+            ? storage_.Create<EqualOperator>(
+                  test_expression, alternative->when_expression->accept(this))
+            : alternative->when_expression->accept(this).as<Expression *>();
+    Expression *then_expression = alternative->then_expression->accept(this);
+    else_expression = storage_.Create<IfOperator>(condition, then_expression,
+                                                  else_expression);
+  }
+  return else_expression;
+}
+
+antlrcpp::Any CypherMainVisitor::visitCaseAlternatives(
+    CypherParser::CaseAlternativesContext *) {
+  debug_fail("Should never be called. See documentation in hpp.");
+  return 0;
+}
+
 antlrcpp::Any CypherMainVisitor::visitWith(CypherParser::WithContext *ctx) {
   auto *with = storage_.Create<With>();
   in_with_ = true;
diff --git a/src/query/frontend/ast/cypher_main_visitor.hpp b/src/query/frontend/ast/cypher_main_visitor.hpp
index 5ad9052c2..c6ca1d50c 100644
--- a/src/query/frontend/ast/cypher_main_visitor.hpp
+++ b/src/query/frontend/ast/cypher_main_visitor.hpp
@@ -521,6 +521,19 @@ class CypherMainVisitor : public antlropencypher::CypherBaseVisitor {
   antlrcpp::Any visitPropertyExpression(
       CypherParser::PropertyExpressionContext *ctx) override;
 
+  /**
+   * @return IfOperator*
+   */
+  antlrcpp::Any visitCaseExpression(
+      CypherParser::CaseExpressionContext *ctx) override;
+
+  /**
+   * Never call this. Ast generation for this production is done in
+   * @c visitCaseExpression.
+   */
+  antlrcpp::Any visitCaseAlternatives(
+      CypherParser::CaseAlternativesContext *ctx) override;
+
   /**
    * @return With*
    */
diff --git a/src/query/frontend/opencypher/grammar/Cypher.g4 b/src/query/frontend/opencypher/grammar/Cypher.g4
index e060e8dc2..192ac6c8e 100644
--- a/src/query/frontend/opencypher/grammar/Cypher.g4
+++ b/src/query/frontend/opencypher/grammar/Cypher.g4
@@ -14,6 +14,13 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
+/*
+ * When changing this grammar make sure to update constants in
+ * src/query/frontend/stripped_lexer_constants.hpp (kKeywords, kSpecialTokens
+ * and bitsets) and src/query/frontend/ast/named_antlr_tokens.hpp if needed.
+ */
+
 grammar Cypher;
 
 cypher : SP? statement ( SP? ';' )? SP? EOF ;
@@ -174,6 +181,7 @@ expression2b : atom ( SP? propertyLookup )* ;
 
 atom : literal
      | parameter
+     | caseExpression
      | ( COUNT SP? '(' SP? '*' SP? ')' )
      | listComprehension
      | patternComprehension
@@ -232,6 +240,10 @@ patternComprehension : '[' SP? ( variable SP? '=' SP? )? relationshipsPattern SP
 
 propertyLookup : '.' SP? ( propertyKeyName ) ;
 
+caseExpression : ( ( CASE ( SP? caseAlternatives )+ ) | ( CASE SP? test=expression ( SP? caseAlternatives )+ ) ) ( SP? ELSE SP? else_expression=expression )? SP? END ;
+
+caseAlternatives : WHEN SP? when_expression=expression SP? THEN SP? then_expression=expression ;
+
 variable : symbolicName ;
 
 StringLiteral : ( '"' ( StringLiteral_0 | EscapedChar )* '"' )
@@ -348,6 +360,11 @@ symbolicName : UnescapedSymbolicName
              | CONTAINS
              | IS
              | CYPHERNULL
+             | CASE
+             | WHEN
+             | THEN
+             | ELSE
+             | END
              | COUNT
              | FILTER
              | EXTRACT
@@ -429,6 +446,16 @@ IS : ( 'I' | 'i' ) ( 'S' | 's' )  ;
 
 CYPHERNULL : ( 'N' | 'n' ) ( 'U' | 'u' ) ( 'L' | 'l' ) ( 'L' | 'l' )  ;
 
+CASE : ( 'C' | 'c' ) ( 'A' | 'a' ) ( 'S' | 's' ) ( 'E' | 'e' )  ;
+
+ELSE : ( 'E' | 'e' ) ( 'L' | 'l' ) ( 'S' | 's' ) ( 'E' | 'e' )  ;
+
+END : ( 'E' | 'e' ) ( 'N' | 'n' ) ( 'D' | 'd' )  ;
+
+WHEN : ( 'W' | 'w' ) ( 'H' | 'h' ) ( 'E' | 'e' ) ( 'N' | 'n' )  ;
+
+THEN : ( 'T' | 't' ) ( 'H' | 'h' ) ( 'E' | 'e' ) ( 'N' | 'n' )  ;
+
 COUNT : ( 'C' | 'c' ) ( 'O' | 'o' ) ( 'U' | 'u' ) ( 'N' | 'n' ) ( 'T' | 't' )  ;
 
 FILTER : ( 'F' | 'f' ) ( 'I' | 'i' ) ( 'L' | 'l' ) ( 'T' | 't' ) ( 'E' | 'e' ) ( 'R' | 'r' )  ;
diff --git a/src/query/frontend/semantic/symbol_generator.cpp b/src/query/frontend/semantic/symbol_generator.cpp
index 5dc34504f..ad8eb20fd 100644
--- a/src/query/frontend/semantic/symbol_generator.cpp
+++ b/src/query/frontend/semantic/symbol_generator.cpp
@@ -254,6 +254,17 @@ bool SymbolGenerator::PreVisit(Aggregation &aggr) {
         "Using aggregation functions inside aggregation functions is not "
         "allowed");
   }
+  if (scope_.num_if_operators) {
+    // Neo allows aggregations here and produces very interesting behaviors.
+    // To simplify implementation at this moment we decided to completely
+    // disallow aggregations inside of the CASE.
+    // However, in some cases aggregation makes perfect sense, for example:
+    //    CASE count(n) WHEN 10 THEN "YES" ELSE "NO" END.
+    // TODO: Rethink of allowing aggregations in some parts of the CASE
+    // construct.
+    throw SemanticException(
+        "Using aggregation functions inside of CASE is not allowed");
+  }
   // Create a virtual symbol for aggregation result.
   // Currently, we only have aggregation operators which return numbers.
   symbol_table_[aggr] =
@@ -268,6 +279,16 @@ bool SymbolGenerator::PostVisit(Aggregation &) {
   return true;
 }
 
+bool SymbolGenerator::PreVisit(IfOperator &) {
+  ++scope_.num_if_operators;
+  return true;
+}
+
+bool SymbolGenerator::PostVisit(IfOperator &) {
+  --scope_.num_if_operators;
+  return true;
+}
+
 bool SymbolGenerator::PreVisit(All &all) {
   all.list_expression_->Accept(*this);
   VisitWithIdentifiers(*all.where_, {all.identifier_});
@@ -412,7 +433,7 @@ bool SymbolGenerator::PreVisit(BreadthFirstAtom &bf_atom) {
   return false;
 }
 
-bool SymbolGenerator::PostVisit(BreadthFirstAtom &bf_atom) {
+bool SymbolGenerator::PostVisit(BreadthFirstAtom &) {
   scope_.visiting_edge = nullptr;
   return true;
 }
diff --git a/src/query/frontend/semantic/symbol_generator.hpp b/src/query/frontend/semantic/symbol_generator.hpp
index 7b924f3de..d151b77d6 100644
--- a/src/query/frontend/semantic/symbol_generator.hpp
+++ b/src/query/frontend/semantic/symbol_generator.hpp
@@ -44,6 +44,8 @@ class SymbolGenerator : public HierarchicalTreeVisitor {
   ReturnType Visit(PrimitiveLiteral &) override { return true; }
   bool PreVisit(Aggregation &) override;
   bool PostVisit(Aggregation &) override;
+  bool PreVisit(IfOperator &) override;
+  bool PostVisit(IfOperator &) override;
   bool PreVisit(All &) override;
 
   // Pattern and its subparts.
@@ -94,6 +96,8 @@ class SymbolGenerator : public HierarchicalTreeVisitor {
     // Match. Identifiers created by naming vertices, edges and paths are *not*
     // stored in here.
     std::vector<Identifier *> identifiers_in_match;
+    // Number of nested IfOperators.
+    int num_if_operators{0};
   };
 
   bool HasSymbol(const std::string &name);
diff --git a/src/query/frontend/semantic/symbol_table.hpp b/src/query/frontend/semantic/symbol_table.hpp
index 5b8eef899..84e63242c 100644
--- a/src/query/frontend/semantic/symbol_table.hpp
+++ b/src/query/frontend/semantic/symbol_table.hpp
@@ -82,4 +82,3 @@ struct hash<query::Symbol> {
 };
 
 }  // namespace std
-
diff --git a/src/query/frontend/stripped_lexer_constants.hpp b/src/query/frontend/stripped_lexer_constants.hpp
index 43b10385c..6751f7468 100644
--- a/src/query/frontend/stripped_lexer_constants.hpp
+++ b/src/query/frontend/stripped_lexer_constants.hpp
@@ -79,14 +79,14 @@ class Trie {
 const int kBitsetSize = 65536;
 
 const trie::Trie kKeywords = {
-    "union",  "all",     "optional",  "match",  "unwind",     "as",
-    "merge",  "on",      "create",    "set",    "detach",     "delete",
-    "remove", "with",    "distinct",  "return", "order",      "by",
-    "skip",   "limit",   "ascending", "asc",    "descending", "desc",
-    "where",  "or",      "xor",       "and",    "not",        "in",
-    "starts", "ends",    "contains",  "is",     "null",       "count",
-    "filter", "extract", "any",       "none",   "single",     "true",
-    "false"};
+    "union",   "all",   "optional",  "match",  "unwind",     "as",
+    "merge",   "on",    "create",    "set",    "detach",     "delete",
+    "remove",  "with",  "distinct",  "return", "order",      "by",
+    "skip",    "limit", "ascending", "asc",    "descending", "desc",
+    "where",   "or",    "xor",       "and",    "not",        "in",
+    "starts",  "ends",  "contains",  "is",     "null",       "case",
+    "when",    "then",  "else",      "end",    "count",      "filter",
+    "extract", "any",   "none",      "single", "true",       "false"};
 
 // Unicode codepoints that are allowed at the start of the unescaped name.
 const std::bitset<kBitsetSize> kUnescapedNameAllowedStarts(std::string(
diff --git a/src/query/interpret/eval.hpp b/src/query/interpret/eval.hpp
index d4de2452d..0c744d07d 100644
--- a/src/query/interpret/eval.hpp
+++ b/src/query/interpret/eval.hpp
@@ -123,6 +123,22 @@ class ExpressionEvaluator : public TreeVisitor<TypedValue> {
     return op.expression2_->Accept(*this);
   }
 
+  TypedValue Visit(IfOperator &if_operator) override {
+    auto condition = if_operator.condition_->Accept(*this);
+    if (condition.IsNull()) {
+      return if_operator.then_expression_->Accept(*this);
+    }
+    if (condition.type() != TypedValue::Type::Bool) {
+      // At the moment IfOperator is used only in CASE construct.
+      throw QueryRuntimeException(
+          "'CASE' expected boolean expression, but got {}", condition.type());
+    }
+    if (condition.Value<bool>()) {
+      return if_operator.then_expression_->Accept(*this);
+    }
+    return if_operator.else_expression_->Accept(*this);
+  }
+
   TypedValue Visit(InListOperator &in_list) override {
     auto literal = in_list.expression1_->Accept(*this);
     auto _list = in_list.expression2_->Accept(*this);
diff --git a/src/query/plan/rule_based_planner.cpp b/src/query/plan/rule_based_planner.cpp
index ff2b8f580..6095ca65f 100644
--- a/src/query/plan/rule_based_planner.cpp
+++ b/src/query/plan/rule_based_planner.cpp
@@ -352,6 +352,23 @@ class ReturnBodyContext : public HierarchicalTreeVisitor {
     return false;
   }
 
+  bool PreVisit(IfOperator &if_operator) override {
+    if_operator.condition_->Accept(*this);
+    bool has_aggr = has_aggregation_.back();
+    has_aggregation_.pop_back();
+    if_operator.then_expression_->Accept(*this);
+    has_aggr = has_aggr || has_aggregation_.back();
+    has_aggregation_.pop_back();
+    if_operator.else_expression_->Accept(*this);
+    has_aggr = has_aggr || has_aggregation_.back();
+    has_aggregation_.pop_back();
+    has_aggregation_.emplace_back(has_aggr);
+    // TODO: Once we allow aggregations here, insert appropriate stuff in
+    // group_by.
+    debug_assert(!has_aggr, "Currently aggregations in CASE are not allowed");
+    return false;
+  }
+
   bool PostVisit(Function &function) override {
     debug_assert(function.arguments_.size() <= has_aggregation_.size(),
                  "Expected has_aggregation_ flags as much as there are "
diff --git a/tests/qa/tck_engine/tests/memgraph_V1/features/case.feature b/tests/qa/tck_engine/tests/memgraph_V1/features/case.feature
new file mode 100644
index 000000000..eae2b28ed
--- /dev/null
+++ b/tests/qa/tck_engine/tests/memgraph_V1/features/case.feature
@@ -0,0 +1,59 @@
+Feature: Case
+
+    Scenario: Simple CASE:
+        Given an empty graph
+        When executing query:
+            """
+            UNWIND range(1, 3) as x RETURN CASE x WHEN 2 THEN "two" END
+            """
+        Then the result should be:
+            |  CASE x WHEN 2 THEN "two" END |
+            |          null                 |
+            |          'two'                |
+            |          null                 |
+
+    Scenario: Simple CASE with ELSE:
+        Given an empty graph
+        When executing query:
+            """
+            UNWIND range(1, 3) as x RETURN CASE x WHEN 2 THEN "two" ELSE "nottwo" END as z
+            """
+        Then the result should be:
+            |    z     |
+            | 'nottwo' |
+            |  'two'   |
+            | 'nottwo' |
+
+    Scenario: Generic CASE:
+        Given an empty graph
+        When executing query:
+            """
+            UNWIND range(1, 3) as x RETURN CASE WHEN x > 1 THEN "greater" END as z
+            """
+        Then the result should be:
+            |    z      |
+            |   null    |
+            | 'greater' |
+            | 'greater' |
+
+    Scenario: Generic CASE multiple matched whens:
+        Given an empty graph
+        When executing query:
+            """
+            UNWIND range(1, 3) as x RETURN CASE WHEN x > 10 THEN 10 WHEN x > 1 THEN 1 WHEN x > 0 THEN 0 WHEN x > "mirko" THEN 1000 END as z
+            """
+        Then the result should be:
+            | z |
+            | 0 |
+            | 1 |
+            | 1 |
+
+    Scenario: Simple CASE in collect:
+        Given an empty graph
+        When executing query:
+            """
+            UNWIND range(1, 3) as x RETURN collect(CASE x WHEN 2 THEN "two" ELSE "nottwo" END) as z
+            """
+        Then the result should be:
+            |           z                 |
+            | ['nottwo', 'two', 'nottwo'] |
diff --git a/tests/qa/tck_engine/tests/memgraph_V1/features/memgraph.feature b/tests/qa/tck_engine/tests/memgraph_V1/features/memgraph.feature
index 4e491c188..bfac9962e 100644
--- a/tests/qa/tck_engine/tests/memgraph_V1/features/memgraph.feature
+++ b/tests/qa/tck_engine/tests/memgraph_V1/features/memgraph.feature
@@ -62,3 +62,11 @@ Feature: Memgraph only tests (queries in which we choose to be incompatible with
             CREATE(a:DELete)
             """
         Then an error should be raised
+
+    Scenario: Aggregation in CASE:
+        Given an empty graph
+        When executing query:
+            """
+            MATCH (n) RETURN CASE count(n) WHEN 10 THEN 10 END
+            """
+        Then an error should be raised
diff --git a/tests/qa/tck_engine/tests/memgraph_V1/features/string_operators.feature b/tests/qa/tck_engine/tests/memgraph_V1/features/string_operators.feature
index 28fec96a5..fba0e49ad 100644
--- a/tests/qa/tck_engine/tests/memgraph_V1/features/string_operators.feature
+++ b/tests/qa/tck_engine/tests/memgraph_V1/features/string_operators.feature
@@ -4,7 +4,7 @@ Feature: String operators
         Given an empty graph
         And having executed
             """
-            CREATE(a{name: "ai'M'e"}), (b{name: "AiMe"}), (c{name: "aime"}) 
+            CREATE(a{name: "ai'M'e"}), (b{name: "AiMe"}), (c{name: "aime"})
             """
         When executing query:
             """
@@ -20,7 +20,7 @@ Feature: String operators
         Given an empty graph
         And having executed
             """
-            CREATE(a{name: "ai'M'e"}), (b{name: "AiMe"}), (c{name: "aime"}) 
+            CREATE(a{name: "ai'M'e"}), (b{name: "AiMe"}), (c{name: "aime"})
             """
         When executing query:
             """
@@ -36,7 +36,7 @@ Feature: String operators
         Given an empty graph
         And having executed
             """
-            CREATE(a{name: 1}), (b{name: 2}), (c{name: null}) 
+            CREATE(a{name: 1}), (b{name: 2}), (c{name: null})
             """
         When executing query:
             """
@@ -50,7 +50,7 @@ Feature: String operators
         Given an empty graph
         And having executed
             """
-            CREATE(a{name: 1}), (b{name: 2}), (c{name: null}) 
+            CREATE(a{name: 1}), (b{name: 2}), (c{name: null})
             """
         When executing query:
             """
@@ -65,7 +65,7 @@ Feature: String operators
         Given an empty graph
         And having executed
             """
-            CREATE(a{name: "ai'M'E"}), (b{name: "AiMe"}), (c{name: "aime"}) 
+            CREATE(a{name: "ai'M'E"}), (b{name: "AiMe"}), (c{name: "aime"})
             """
         When executing query:
             """
@@ -82,7 +82,7 @@ Feature: String operators
         Given an empty graph
         And having executed
             """
-            CREATE(a{name: "ai'M'e"}), (b{name: "AiMe"}), (c{name: "aime"}) 
+            CREATE(a{name: "ai'M'e"}), (b{name: "AiMe"}), (c{name: "aime"})
             """
         When executing query:
             """
@@ -98,7 +98,7 @@ Feature: String operators
         Given an empty graph
         And having executed
             """
-            CREATE(a{name: 1}), (b{name: 2}), (c{name: null}) 
+            CREATE(a{name: 1}), (b{name: 2}), (c{name: null})
             """
         When executing query:
             """
@@ -112,7 +112,7 @@ Feature: String operators
         Given an empty graph
         And having executed
             """
-            CREATE(a{name: 1}), (b{name: 2}), (c{name: null}) 
+            CREATE(a{name: 1}), (b{name: 2}), (c{name: null})
             """
         When executing query:
             """
@@ -127,7 +127,7 @@ Feature: String operators
         Given an empty graph
         And having executed
             """
-            CREATE(a{name: "ai'M'e"}), (b{name: "AiMe"}), (c{name: "aime"}) 
+            CREATE(a{name: "ai'M'e"}), (b{name: "AiMe"}), (c{name: "aime"})
             """
         When executing query:
             """
@@ -143,7 +143,7 @@ Feature: String operators
         Given an empty graph
         And having executed
             """
-            CREATE(a{name: "ai'M'e"}), (b{name: "AiMe"}), (c{name: "aime"}) 
+            CREATE(a{name: "ai'M'e"}), (b{name: "AiMe"}), (c{name: "aime"})
             """
         When executing query:
             """
@@ -159,7 +159,7 @@ Feature: String operators
         Given an empty graph
         And having executed
             """
-            CREATE(a{name: 1}), (b{name: 2}), (c{name: null}) 
+            CREATE(a{name: 1}), (b{name: 2}), (c{name: null})
             """
         When executing query:
             """
@@ -169,12 +169,12 @@ Feature: String operators
             """
         Then an error should be raised
 
-    
+
     Scenario: Contains test5
         Given an empty graph
         And having executed
             """
-            CREATE(a{name: 1}), (b{name: 2}), (c{name: null}) 
+            CREATE(a{name: 1}), (b{name: 2}), (c{name: null})
             """
         When executing query:
             """
@@ -183,4 +183,3 @@ Feature: String operators
             return n.name
             """
         Then an error should be raised
-
diff --git a/tests/qa/tck_engine/tests/memgraph_V1/features/unstable.feature b/tests/qa/tck_engine/tests/memgraph_V1/features/unstable.feature
index e6c396d0e..6ca3f1dba 100644
--- a/tests/qa/tck_engine/tests/memgraph_V1/features/unstable.feature
+++ b/tests/qa/tck_engine/tests/memgraph_V1/features/unstable.feature
@@ -59,7 +59,7 @@ Feature: Unstable
         Given an empty graph
         And having executed
             """
-            CREATE(a{name: 1}), (b{name: 2}), (c{name: null}) 
+            CREATE(a{name: 1}), (b{name: 2}), (c{name: null})
             """
         When executing query:
             """
@@ -73,7 +73,7 @@ Feature: Unstable
         Given an empty graph
         And having executed
             """
-            CREATE(a{name: 1}), (b{name: 2}), (c{name: null}) 
+            CREATE(a{name: 1}), (b{name: 2}), (c{name: null})
             """
         When executing query:
             """
@@ -87,7 +87,7 @@ Feature: Unstable
         Given an empty graph
         And having executed
             """
-            CREATE(a{name: 1}), (b{name: 2}), (c{name: null}) 
+            CREATE(a{name: 1}), (b{name: 2}), (c{name: null})
             """
         When executing query:
             """
@@ -105,4 +105,3 @@ Feature: Unstable
         Then the result should be:
             | n                   |
             | 2.718281828459045   |
-
diff --git a/tests/unit/cypher_main_visitor.cpp b/tests/unit/cypher_main_visitor.cpp
index f8a1db10e..07b521419 100644
--- a/tests/unit/cypher_main_visitor.cpp
+++ b/tests/unit/cypher_main_visitor.cpp
@@ -541,6 +541,76 @@ TYPED_TEST(CypherMainVisitorTest, InWithListIndexing) {
   EXPECT_TRUE(list_index);
 }
 
+TYPED_TEST(CypherMainVisitorTest, CaseGenericForm) {
+  TypeParam ast_generator(
+      "RETURN CASE WHEN n < 10 THEN 1 WHEN n > 10 THEN 2 END");
+  auto *query = ast_generator.query_;
+  auto *return_clause = dynamic_cast<Return *>(query->clauses_[0]);
+  auto *if_operator = dynamic_cast<IfOperator *>(
+      return_clause->body_.named_expressions[0]->expression_);
+  ASSERT_TRUE(if_operator);
+  auto *condition = dynamic_cast<LessOperator *>(if_operator->condition_);
+  ASSERT_TRUE(condition);
+  auto *then_expression =
+      dynamic_cast<PrimitiveLiteral *>(if_operator->then_expression_);
+  ASSERT_TRUE(then_expression);
+  ASSERT_EQ(then_expression->value_.Value<int64_t>(), 1);
+
+  auto *if_operator2 =
+      dynamic_cast<IfOperator *>(if_operator->else_expression_);
+  ASSERT_TRUE(if_operator2);
+  auto *condition2 = dynamic_cast<GreaterOperator *>(if_operator2->condition_);
+  ASSERT_TRUE(condition2);
+  auto *then_expression2 =
+      dynamic_cast<PrimitiveLiteral *>(if_operator2->then_expression_);
+  ASSERT_TRUE(then_expression2);
+  ASSERT_EQ(then_expression2->value_.Value<int64_t>(), 2);
+  auto *else_expression2 =
+      dynamic_cast<PrimitiveLiteral *>(if_operator2->else_expression_);
+  ASSERT_TRUE(else_expression2);
+  ASSERT_TRUE(else_expression2->value_.IsNull());
+}
+
+TYPED_TEST(CypherMainVisitorTest, CaseGenericFormElse) {
+  TypeParam ast_generator("RETURN CASE WHEN n < 10 THEN 1 ELSE 2 END");
+  auto *query = ast_generator.query_;
+  auto *return_clause = dynamic_cast<Return *>(query->clauses_[0]);
+  auto *if_operator = dynamic_cast<IfOperator *>(
+      return_clause->body_.named_expressions[0]->expression_);
+  auto *condition = dynamic_cast<LessOperator *>(if_operator->condition_);
+  ASSERT_TRUE(condition);
+  auto *then_expression =
+      dynamic_cast<PrimitiveLiteral *>(if_operator->then_expression_);
+  ASSERT_EQ(then_expression->value_.Value<int64_t>(), 1);
+  auto *else_expression =
+      dynamic_cast<PrimitiveLiteral *>(if_operator->else_expression_);
+  ASSERT_TRUE(else_expression);
+  ASSERT_EQ(else_expression->value_.Value<int64_t>(), 2);
+}
+
+TYPED_TEST(CypherMainVisitorTest, CaseSimpleForm) {
+  TypeParam ast_generator("RETURN CASE 5 WHEN 10 THEN 1 END");
+  auto *query = ast_generator.query_;
+  auto *return_clause = dynamic_cast<Return *>(query->clauses_[0]);
+  auto *if_operator = dynamic_cast<IfOperator *>(
+      return_clause->body_.named_expressions[0]->expression_);
+  auto *condition = dynamic_cast<EqualOperator *>(if_operator->condition_);
+  ASSERT_TRUE(condition);
+  auto *expr1 = dynamic_cast<PrimitiveLiteral *>(condition->expression1_);
+  ASSERT_TRUE(expr1);
+  ASSERT_EQ(expr1->value_.Value<int64_t>(), 5);
+  auto *expr2 = dynamic_cast<PrimitiveLiteral *>(condition->expression2_);
+  ASSERT_TRUE(expr2);
+  ASSERT_EQ(expr2->value_.Value<int64_t>(), 10);
+  auto *then_expression =
+      dynamic_cast<PrimitiveLiteral *>(if_operator->then_expression_);
+  ASSERT_EQ(then_expression->value_.Value<int64_t>(), 1);
+  auto *else_expression =
+      dynamic_cast<PrimitiveLiteral *>(if_operator->else_expression_);
+  ASSERT_TRUE(else_expression);
+  ASSERT_TRUE(else_expression->value_.IsNull());
+}
+
 TYPED_TEST(CypherMainVisitorTest, IsNull) {
   TypeParam ast_generator("RETURN 2 iS NulL");
   auto *query = ast_generator.query_;
diff --git a/tests/unit/query_expression_evaluator.cpp b/tests/unit/query_expression_evaluator.cpp
index 9b7a71612..3abb26989 100644
--- a/tests/unit/query_expression_evaluator.cpp
+++ b/tests/unit/query_expression_evaluator.cpp
@@ -459,6 +459,39 @@ TEST(ExpressionEvaluator, ListSlicingOperator) {
   }
 }
 
+TEST(ExpressionEvaluator, IfOperator) {
+  AstTreeStorage storage;
+  NoContextExpressionEvaluator eval;
+  auto *then_expression = storage.Create<PrimitiveLiteral>(10);
+  auto *else_expression = storage.Create<PrimitiveLiteral>(20);
+  {
+    auto *condition_true =
+        storage.Create<EqualOperator>(storage.Create<PrimitiveLiteral>(2),
+                                      storage.Create<PrimitiveLiteral>(2));
+    auto *op = storage.Create<IfOperator>(condition_true, then_expression,
+                                          else_expression);
+    auto value = op->Accept(eval.eval);
+    ASSERT_EQ(value.Value<int64_t>(), 10);
+  }
+  {
+    auto *condition_false =
+        storage.Create<EqualOperator>(storage.Create<PrimitiveLiteral>(2),
+                                      storage.Create<PrimitiveLiteral>(3));
+    auto *op = storage.Create<IfOperator>(condition_false, then_expression,
+                                          else_expression);
+    auto value = op->Accept(eval.eval);
+    ASSERT_EQ(value.Value<int64_t>(), 20);
+  }
+  {
+    auto *condition_exception =
+        storage.Create<AdditionOperator>(storage.Create<PrimitiveLiteral>(2),
+                                         storage.Create<PrimitiveLiteral>(3));
+    auto *op = storage.Create<IfOperator>(condition_exception, then_expression,
+                                          else_expression);
+    ASSERT_THROW(op->Accept(eval.eval), QueryRuntimeException);
+  }
+}
+
 TEST(ExpressionEvaluator, NotOperator) {
   AstTreeStorage storage;
   NoContextExpressionEvaluator eval;