From 22d1b3164f54563a2ab8a803b582831832372378 Mon Sep 17 00:00:00 2001 From: ByteBlast Date: Tue, 7 Oct 2014 23:03:12 +0100 Subject: [PATCH 01/17] Beginnings of #25. --- .../ViewResultTestTests.cs | 24 +++++++++++++++++-- TestStack.FluentMvcTesting/ViewResultTest.cs | 17 ++++++++++--- 2 files changed, 36 insertions(+), 5 deletions(-) diff --git a/TestStack.FluentMVCTesting.Tests/ViewResultTestTests.cs b/TestStack.FluentMVCTesting.Tests/ViewResultTestTests.cs index 56ee166..87647c5 100644 --- a/TestStack.FluentMVCTesting.Tests/ViewResultTestTests.cs +++ b/TestStack.FluentMVCTesting.Tests/ViewResultTestTests.cs @@ -1,4 +1,5 @@ -using System.Web.Mvc; +using System.Text.RegularExpressions; +using System.Web.Mvc; using NUnit.Framework; namespace TestStack.FluentMVCTesting.Tests @@ -71,7 +72,26 @@ public void Check_for_invalid_model_using_predicate() var exception = Assert.Throws(() => _viewResultTest.WithModel(m => m.Property1 == null) ); - Assert.That(exception.Message, Is.EqualTo("Expected view model to pass the given condition, but it failed.")); + Assert.That(exception.Message, Is.EqualTo(string.Format("Expected view model {{\"Property1\":\"{0}\",\"Property2\":{1}}} to pass the given condition (m => m.Property1 == null), but it failed.", _model.Property1, _model.Property2))); + } + + [Test] + public void Check_for_invalid_model_using_predicate_with_two_conditions() + { + var exception = Assert.Throws(() => + _viewResultTest.WithModel(m => m.Property1 == null && m.Property2 == 2) + ); + Assert.That(exception.Message, Is.EqualTo(string.Format("Expected view model {{\"Property1\":\"{0}\",\"Property2\":{1}}} to pass the given condition (m => m.Property1 == null && m.Property2 == 2), but it failed.", _model.Property1, _model.Property2))); + } + + [Test] + public void Check_for_invalid_model_using_predicate_with_closure() + { + int capturedOuterVariable = 2; + var exception = Assert.Throws(() => + _viewResultTest.WithModel(m => m.Property1 == null && m.Property2 == capturedOuterVariable) + ); + Assert.That(exception.Message, Is.EqualTo(string.Format("Expected view model {{\"Property1\":\"{0}\",\"Property2\":{1}}} to pass the given condition (m => m.Property1 == null AndAlso m.Property2 == 2), but it failed.", _model.Property1, capturedOuterVariable))); } [Test] diff --git a/TestStack.FluentMvcTesting/ViewResultTest.cs b/TestStack.FluentMvcTesting/ViewResultTest.cs index 3f13236..aefb1d7 100644 --- a/TestStack.FluentMvcTesting/ViewResultTest.cs +++ b/TestStack.FluentMvcTesting/ViewResultTest.cs @@ -1,4 +1,7 @@ using System; +using System.Linq.Expressions; +using System.Text.RegularExpressions; +using System.Web.Helpers; using System.Web.Mvc; namespace TestStack.FluentMVCTesting @@ -37,13 +40,21 @@ public ModelTest WithModel(TModel expectedModel) where TModel : return test; } - public ModelTest WithModel(Func predicate) where TModel : class + public ModelTest WithModel(Expression> predicate) where TModel : class { var test = WithModel(); var model = _viewResult.Model as TModel; - if (!predicate(model)) - throw new ViewResultModelAssertionException("Expected view model to pass the given condition, but it failed."); + + var modelLex = Json.Encode(model); + var predicateLex = Regex.Replace(predicate.ToString(), "[()]", ""); + var compiledPredicate = predicate.Compile(); + + if (!compiledPredicate(model)) + throw new ViewResultModelAssertionException(string.Format( + "Expected view model {0} to pass the given condition ({1}), but it failed.", + modelLex, + predicateLex)); return test; } From e4d34a4bcb365912b9455572e9e532968b3c9833 Mon Sep 17 00:00:00 2001 From: ByteBlast Date: Sun, 12 Oct 2014 05:25:05 +0100 Subject: [PATCH 02/17] Foundation of the ExpressionVisitor. --- .../Internal/ExpressionInspectorTests.cs | 10 ++++++++++ .../TestStack.FluentMVCTesting.Tests.csproj | 1 + .../Internal/ExpressionInspector.cs | 13 +++++++++++++ .../Properties/AssemblyInfo.cs | 1 + .../TestStack.FluentMVCTesting.csproj | 1 + 5 files changed, 26 insertions(+) create mode 100644 TestStack.FluentMVCTesting.Tests/Internal/ExpressionInspectorTests.cs create mode 100644 TestStack.FluentMvcTesting/Internal/ExpressionInspector.cs diff --git a/TestStack.FluentMVCTesting.Tests/Internal/ExpressionInspectorTests.cs b/TestStack.FluentMVCTesting.Tests/Internal/ExpressionInspectorTests.cs new file mode 100644 index 0000000..4a175fb --- /dev/null +++ b/TestStack.FluentMVCTesting.Tests/Internal/ExpressionInspectorTests.cs @@ -0,0 +1,10 @@ +using NUnit.Framework; + +namespace TestStack.FluentMVCTesting.Tests.Internal +{ + [TestFixture] + public class ExpressionInspectorTests + { + + } +} \ No newline at end of file diff --git a/TestStack.FluentMVCTesting.Tests/TestStack.FluentMVCTesting.Tests.csproj b/TestStack.FluentMVCTesting.Tests/TestStack.FluentMVCTesting.Tests.csproj index 5129022..2db7429 100644 --- a/TestStack.FluentMVCTesting.Tests/TestStack.FluentMVCTesting.Tests.csproj +++ b/TestStack.FluentMVCTesting.Tests/TestStack.FluentMVCTesting.Tests.csproj @@ -89,6 +89,7 @@ + diff --git a/TestStack.FluentMvcTesting/Internal/ExpressionInspector.cs b/TestStack.FluentMvcTesting/Internal/ExpressionInspector.cs new file mode 100644 index 0000000..c6f0d09 --- /dev/null +++ b/TestStack.FluentMvcTesting/Internal/ExpressionInspector.cs @@ -0,0 +1,13 @@ +using System; +using System.Linq.Expressions; + +namespace TestStack.FluentMVCTesting.Internal +{ + internal class ExpressionInspector + { + internal void Inspect(LambdaExpression expression) + { + throw new NotImplementedException(); + } + } +} \ No newline at end of file diff --git a/TestStack.FluentMvcTesting/Properties/AssemblyInfo.cs b/TestStack.FluentMvcTesting/Properties/AssemblyInfo.cs index 7e935da..a4b5e0a 100644 --- a/TestStack.FluentMvcTesting/Properties/AssemblyInfo.cs +++ b/TestStack.FluentMvcTesting/Properties/AssemblyInfo.cs @@ -34,3 +34,4 @@ // [assembly: AssemblyVersion("1.0.*")] [assembly: AssemblyVersion("1.0.0.0")] [assembly: AssemblyFileVersion("1.0.0.0")] +[assembly: InternalsVisibleTo("TestStack.FluentMVCTesting.Tests")] diff --git a/TestStack.FluentMvcTesting/TestStack.FluentMVCTesting.csproj b/TestStack.FluentMvcTesting/TestStack.FluentMVCTesting.csproj index 553b197..5789a34 100644 --- a/TestStack.FluentMvcTesting/TestStack.FluentMVCTesting.csproj +++ b/TestStack.FluentMvcTesting/TestStack.FluentMVCTesting.csproj @@ -83,6 +83,7 @@ + From 242e082911518e7f0609aae8bfbeebc9660a3760 Mon Sep 17 00:00:00 2001 From: ByteBlast Date: Tue, 14 Oct 2014 17:21:35 +0100 Subject: [PATCH 03/17] Support for parsing equality operator with primitive operands. --- .../Internal/ExpressionInspectorTests.cs | 23 +++++++++++++++++-- .../Internal/ExpressionInspector.cs | 8 +++---- 2 files changed, 25 insertions(+), 6 deletions(-) diff --git a/TestStack.FluentMVCTesting.Tests/Internal/ExpressionInspectorTests.cs b/TestStack.FluentMVCTesting.Tests/Internal/ExpressionInspectorTests.cs index 4a175fb..8eb146b 100644 --- a/TestStack.FluentMVCTesting.Tests/Internal/ExpressionInspectorTests.cs +++ b/TestStack.FluentMVCTesting.Tests/Internal/ExpressionInspectorTests.cs @@ -1,10 +1,29 @@ using NUnit.Framework; +using System; +using System.Linq.Expressions; +using TestStack.FluentMVCTesting.Internal; namespace TestStack.FluentMVCTesting.Tests.Internal { [TestFixture] - public class ExpressionInspectorTests + public class ExpressionInspectorShould { - + [Test] + public void Correctly_parse_equality_comparison_with_string_operands() + { + Expression> func = text => text == "any"; + ExpressionInspector sut = new ExpressionInspector(); + var actual = sut.Inspect(func); + Assert.AreEqual("text => text == \"any\"", actual); + } + + [Test] + public void Correctly_parse_equality_comparison_with_int_operands() + { + Expression> func = number => number == 5; + ExpressionInspector sut = new ExpressionInspector(); + var actual = sut.Inspect(func); + Assert.AreEqual("number => number == 5", actual); + } } } \ No newline at end of file diff --git a/TestStack.FluentMvcTesting/Internal/ExpressionInspector.cs b/TestStack.FluentMvcTesting/Internal/ExpressionInspector.cs index c6f0d09..c2f0cfa 100644 --- a/TestStack.FluentMvcTesting/Internal/ExpressionInspector.cs +++ b/TestStack.FluentMvcTesting/Internal/ExpressionInspector.cs @@ -1,13 +1,13 @@ -using System; -using System.Linq.Expressions; +using System.Linq.Expressions; +using System.Text.RegularExpressions; namespace TestStack.FluentMVCTesting.Internal { internal class ExpressionInspector { - internal void Inspect(LambdaExpression expression) + internal string Inspect(LambdaExpression expression) { - throw new NotImplementedException(); + return Regex.Replace(expression.ToString(), "[()]", ""); } } } \ No newline at end of file From f6a35c5490f1d35db4a3c22bb60ec188a3f88ac4 Mon Sep 17 00:00:00 2001 From: ByteBlast Date: Tue, 14 Oct 2014 17:21:58 +0100 Subject: [PATCH 04/17] Support for parsing inequality operator with integral operands. --- .../Internal/ExpressionInspectorTests.cs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/TestStack.FluentMVCTesting.Tests/Internal/ExpressionInspectorTests.cs b/TestStack.FluentMVCTesting.Tests/Internal/ExpressionInspectorTests.cs index 8eb146b..96b4aeb 100644 --- a/TestStack.FluentMVCTesting.Tests/Internal/ExpressionInspectorTests.cs +++ b/TestStack.FluentMVCTesting.Tests/Internal/ExpressionInspectorTests.cs @@ -25,5 +25,14 @@ public void Correctly_parse_equality_comparison_with_int_operands() var actual = sut.Inspect(func); Assert.AreEqual("number => number == 5", actual); } + + [Test] + public void Correctly_parse_inequality_comparison_with_int_operands() + { + Expression> func = number => number != 5; + ExpressionInspector sut = new ExpressionInspector(); + var actual = sut.Inspect(func); + Assert.AreEqual("number => number != 5", actual); + } } } \ No newline at end of file From 5fedb9db6b818ad72d8461c2e74904af40f02622 Mon Sep 17 00:00:00 2001 From: ByteBlast Date: Tue, 14 Oct 2014 17:23:51 +0100 Subject: [PATCH 05/17] Support for parsing equality operator with constant operand. --- .../Internal/ExpressionInspectorTests.cs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/TestStack.FluentMVCTesting.Tests/Internal/ExpressionInspectorTests.cs b/TestStack.FluentMVCTesting.Tests/Internal/ExpressionInspectorTests.cs index 96b4aeb..fa61390 100644 --- a/TestStack.FluentMVCTesting.Tests/Internal/ExpressionInspectorTests.cs +++ b/TestStack.FluentMVCTesting.Tests/Internal/ExpressionInspectorTests.cs @@ -34,5 +34,15 @@ public void Correctly_parse_inequality_comparison_with_int_operands() var actual = sut.Inspect(func); Assert.AreEqual("number => number != 5", actual); } + + [Test] + public void Correctly_parse_equality_comparison_with_captured_constant_operand() + { + const int Number = 5; + Expression> func = number => number == Number; + ExpressionInspector sut = new ExpressionInspector(); + var actual = sut.Inspect(func); + Assert.AreEqual("number => number == " + Number, actual); + } } } \ No newline at end of file From 47486bfc257b0783082bbfcd05cdeee89c672607 Mon Sep 17 00:00:00 2001 From: ByteBlast Date: Tue, 14 Oct 2014 17:27:34 +0100 Subject: [PATCH 06/17] Support for parsing relational operator. --- .../Internal/ExpressionInspectorTests.cs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/TestStack.FluentMVCTesting.Tests/Internal/ExpressionInspectorTests.cs b/TestStack.FluentMVCTesting.Tests/Internal/ExpressionInspectorTests.cs index fa61390..32fab45 100644 --- a/TestStack.FluentMVCTesting.Tests/Internal/ExpressionInspectorTests.cs +++ b/TestStack.FluentMVCTesting.Tests/Internal/ExpressionInspectorTests.cs @@ -27,7 +27,7 @@ public void Correctly_parse_equality_comparison_with_int_operands() } [Test] - public void Correctly_parse_inequality_comparison_with_int_operands() + public void Correctly_parse_inequality_comparison() { Expression> func = number => number != 5; ExpressionInspector sut = new ExpressionInspector(); @@ -44,5 +44,14 @@ public void Correctly_parse_equality_comparison_with_captured_constant_operand() var actual = sut.Inspect(func); Assert.AreEqual("number => number == " + Number, actual); } + + [Test] + public void Correctly_parse_relational_comparison() + { + Expression> func = number => number < 5; + ExpressionInspector sut = new ExpressionInspector(); + var actual = sut.Inspect(func); + Assert.AreEqual("number => number < 5", actual); + } } } \ No newline at end of file From 47566facd80b077b6858aca3eec715743b3bbd45 Mon Sep 17 00:00:00 2001 From: ByteBlast Date: Tue, 14 Oct 2014 17:31:42 +0100 Subject: [PATCH 07/17] Support for parsing equality operator with property operands. --- .../Internal/ExpressionInspectorTests.cs | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/TestStack.FluentMVCTesting.Tests/Internal/ExpressionInspectorTests.cs b/TestStack.FluentMVCTesting.Tests/Internal/ExpressionInspectorTests.cs index 32fab45..b4975d4 100644 --- a/TestStack.FluentMVCTesting.Tests/Internal/ExpressionInspectorTests.cs +++ b/TestStack.FluentMVCTesting.Tests/Internal/ExpressionInspectorTests.cs @@ -26,6 +26,24 @@ public void Correctly_parse_equality_comparison_with_int_operands() Assert.AreEqual("number => number == 5", actual); } + [Test] + public void Correctly_parse_equality_comparison_with_property_operand() + { + Expression> func = post => post.Title == "A"; + ExpressionInspector sut = new ExpressionInspector(); + var actual = sut.Inspect(func); + Assert.AreEqual("post => post.Title == \"A\"", actual); + } + + [Test] + public void Correctly_parse_equality_comparison_with_property_operands() + { + Expression> func = post => post.Title == post.Slug; + ExpressionInspector sut = new ExpressionInspector(); + var actual = sut.Inspect(func); + Assert.AreEqual("post => post.Title == post.Slug", actual); + } + [Test] public void Correctly_parse_inequality_comparison() { @@ -54,4 +72,10 @@ public void Correctly_parse_relational_comparison() Assert.AreEqual("number => number < 5", actual); } } + + public class PostViewModel + { + public string Title { get; set; } + public string Slug { get; set; } + } } \ No newline at end of file From f49642d3b5132cadb9530f1adbf669f50124ebb7 Mon Sep 17 00:00:00 2001 From: ByteBlast Date: Fri, 17 Oct 2014 22:11:08 +0100 Subject: [PATCH 08/17] Support for parsing conditional or operator. --- .../Internal/ExpressionInspectorTests.cs | 31 +++++++++++++++++++ .../Internal/ExpressionInspector.cs | 3 +- 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/TestStack.FluentMVCTesting.Tests/Internal/ExpressionInspectorTests.cs b/TestStack.FluentMVCTesting.Tests/Internal/ExpressionInspectorTests.cs index b4975d4..70e7855 100644 --- a/TestStack.FluentMVCTesting.Tests/Internal/ExpressionInspectorTests.cs +++ b/TestStack.FluentMVCTesting.Tests/Internal/ExpressionInspectorTests.cs @@ -71,11 +71,42 @@ public void Correctly_parse_relational_comparison() var actual = sut.Inspect(func); Assert.AreEqual("number => number < 5", actual); } + + [Test] + public void Correctly_parse_conditional_or_operator() + { + Expression> func = + text => text == "any" || text.Length == 3; + ExpressionInspector sut = new ExpressionInspector(); + var actual = sut.Inspect(func); + Assert.AreEqual("text => text == \"any\" || text.Length == 3", actual); + } + + public void Correctly_parse_two_conditional_or_operators() + { + Expression> func = + text => text == "any" || text.Length == 3 || text.Length == 9; + ExpressionInspector sut = new ExpressionInspector(); + var actual = sut.Inspect(func); + Assert.AreEqual("text => text == \"any\" || text.Length == 3 || text.Length == 9", actual); + } + + [Test] + public void Not_mistake_property_called_OrElse_for_conditional_or_operator() + { + Expression> func = + post => post.Title == "" || post.OrElse == ""; + ExpressionInspector sut = new ExpressionInspector(); + var actual = sut.Inspect(func); + Assert.AreEqual("post => post.Title == \"\" || post.OrElse == \"\"", actual); + } } public class PostViewModel { public string Title { get; set; } public string Slug { get; set; } + + public string OrElse { get; set; } } } \ No newline at end of file diff --git a/TestStack.FluentMvcTesting/Internal/ExpressionInspector.cs b/TestStack.FluentMvcTesting/Internal/ExpressionInspector.cs index c2f0cfa..7607517 100644 --- a/TestStack.FluentMvcTesting/Internal/ExpressionInspector.cs +++ b/TestStack.FluentMvcTesting/Internal/ExpressionInspector.cs @@ -7,7 +7,8 @@ internal class ExpressionInspector { internal string Inspect(LambdaExpression expression) { - return Regex.Replace(expression.ToString(), "[()]", ""); + return Regex.Replace(expression.ToString(), "[()]", "") + .Replace(" OrElse ", " || "); } } } \ No newline at end of file From 18f018a13296b48fdf248209f45c893fce8d7505 Mon Sep 17 00:00:00 2001 From: ByteBlast Date: Fri, 17 Oct 2014 22:43:25 +0100 Subject: [PATCH 09/17] Added failing test. --- .../Internal/ExpressionInspectorTests.cs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/TestStack.FluentMVCTesting.Tests/Internal/ExpressionInspectorTests.cs b/TestStack.FluentMVCTesting.Tests/Internal/ExpressionInspectorTests.cs index 70e7855..a479fe8 100644 --- a/TestStack.FluentMVCTesting.Tests/Internal/ExpressionInspectorTests.cs +++ b/TestStack.FluentMVCTesting.Tests/Internal/ExpressionInspectorTests.cs @@ -82,6 +82,7 @@ public void Correctly_parse_conditional_or_operator() Assert.AreEqual("text => text == \"any\" || text.Length == 3", actual); } + [Test] public void Correctly_parse_two_conditional_or_operators() { Expression> func = @@ -100,6 +101,15 @@ public void Not_mistake_property_called_OrElse_for_conditional_or_operator() var actual = sut.Inspect(func); Assert.AreEqual("post => post.Title == \"\" || post.OrElse == \"\"", actual); } + + [Test] + public void Correctly_parse_expression_whose_source_text_contains_parentheses() + { + Expression> func = post => post.Title.Contains(""); + ExpressionInspector sut = new ExpressionInspector(); + var actual = sut.Inspect(func); + Assert.AreEqual("post => post.Title.Contains(\"\")", actual); + } } public class PostViewModel From e6ecea907a84058cccaadf66ac94cbacb0894ce2 Mon Sep 17 00:00:00 2001 From: ByteBlast Date: Sat, 18 Oct 2014 09:52:25 +0100 Subject: [PATCH 10/17] Tweaked test specification. Originally I had intended to remove any extraneous parentheses such as those that surround language expressions. Unfortunately, this proved to be more difficult than I had anticipated as it is hard to differentiate said extraneous parentheses from meaningful ones such as those used to invoke a method. It is for these reasons that I have changed the test specification to allow extraneous parentheses. --- .../Internal/ExpressionInspectorTests.cs | 21 ++++++++++--------- .../Internal/ExpressionInspector.cs | 2 +- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/TestStack.FluentMVCTesting.Tests/Internal/ExpressionInspectorTests.cs b/TestStack.FluentMVCTesting.Tests/Internal/ExpressionInspectorTests.cs index a479fe8..8abf890 100644 --- a/TestStack.FluentMVCTesting.Tests/Internal/ExpressionInspectorTests.cs +++ b/TestStack.FluentMVCTesting.Tests/Internal/ExpressionInspectorTests.cs @@ -14,7 +14,7 @@ public void Correctly_parse_equality_comparison_with_string_operands() Expression> func = text => text == "any"; ExpressionInspector sut = new ExpressionInspector(); var actual = sut.Inspect(func); - Assert.AreEqual("text => text == \"any\"", actual); + Assert.AreEqual("text => (text == \"any\")", actual); } [Test] @@ -23,7 +23,7 @@ public void Correctly_parse_equality_comparison_with_int_operands() Expression> func = number => number == 5; ExpressionInspector sut = new ExpressionInspector(); var actual = sut.Inspect(func); - Assert.AreEqual("number => number == 5", actual); + Assert.AreEqual("number => (number == 5)", actual); } [Test] @@ -32,7 +32,7 @@ public void Correctly_parse_equality_comparison_with_property_operand() Expression> func = post => post.Title == "A"; ExpressionInspector sut = new ExpressionInspector(); var actual = sut.Inspect(func); - Assert.AreEqual("post => post.Title == \"A\"", actual); + Assert.AreEqual("post => (post.Title == \"A\")", actual); } [Test] @@ -41,7 +41,7 @@ public void Correctly_parse_equality_comparison_with_property_operands() Expression> func = post => post.Title == post.Slug; ExpressionInspector sut = new ExpressionInspector(); var actual = sut.Inspect(func); - Assert.AreEqual("post => post.Title == post.Slug", actual); + Assert.AreEqual("post => (post.Title == post.Slug)", actual); } [Test] @@ -50,7 +50,7 @@ public void Correctly_parse_inequality_comparison() Expression> func = number => number != 5; ExpressionInspector sut = new ExpressionInspector(); var actual = sut.Inspect(func); - Assert.AreEqual("number => number != 5", actual); + Assert.AreEqual("number => (number != 5)", actual); } [Test] @@ -60,7 +60,8 @@ public void Correctly_parse_equality_comparison_with_captured_constant_operand() Expression> func = number => number == Number; ExpressionInspector sut = new ExpressionInspector(); var actual = sut.Inspect(func); - Assert.AreEqual("number => number == " + Number, actual); + Assert.AreEqual( + string.Concat("number => (number == ", Number, ")"), actual); } [Test] @@ -69,7 +70,7 @@ public void Correctly_parse_relational_comparison() Expression> func = number => number < 5; ExpressionInspector sut = new ExpressionInspector(); var actual = sut.Inspect(func); - Assert.AreEqual("number => number < 5", actual); + Assert.AreEqual("number => (number < 5)", actual); } [Test] @@ -79,7 +80,7 @@ public void Correctly_parse_conditional_or_operator() text => text == "any" || text.Length == 3; ExpressionInspector sut = new ExpressionInspector(); var actual = sut.Inspect(func); - Assert.AreEqual("text => text == \"any\" || text.Length == 3", actual); + Assert.AreEqual("text => ((text == \"any\") || (text.Length == 3))", actual); } [Test] @@ -89,7 +90,7 @@ public void Correctly_parse_two_conditional_or_operators() text => text == "any" || text.Length == 3 || text.Length == 9; ExpressionInspector sut = new ExpressionInspector(); var actual = sut.Inspect(func); - Assert.AreEqual("text => text == \"any\" || text.Length == 3 || text.Length == 9", actual); + Assert.AreEqual("text => (((text == \"any\") || (text.Length == 3)) || (text.Length == 9))", actual); } [Test] @@ -99,7 +100,7 @@ public void Not_mistake_property_called_OrElse_for_conditional_or_operator() post => post.Title == "" || post.OrElse == ""; ExpressionInspector sut = new ExpressionInspector(); var actual = sut.Inspect(func); - Assert.AreEqual("post => post.Title == \"\" || post.OrElse == \"\"", actual); + Assert.AreEqual("post => ((post.Title == \"\") || (post.OrElse == \"\"))", actual); } [Test] diff --git a/TestStack.FluentMvcTesting/Internal/ExpressionInspector.cs b/TestStack.FluentMvcTesting/Internal/ExpressionInspector.cs index 7607517..5c2f30f 100644 --- a/TestStack.FluentMvcTesting/Internal/ExpressionInspector.cs +++ b/TestStack.FluentMvcTesting/Internal/ExpressionInspector.cs @@ -7,7 +7,7 @@ internal class ExpressionInspector { internal string Inspect(LambdaExpression expression) { - return Regex.Replace(expression.ToString(), "[()]", "") + return expression.ToString() .Replace(" OrElse ", " || "); } } From e5a25308af9becb26ff7ba6e6bfc1258ead6fce2 Mon Sep 17 00:00:00 2001 From: ByteBlast Date: Sat, 18 Oct 2014 09:59:05 +0100 Subject: [PATCH 11/17] Support for parsing conditional and operator. --- .../Internal/ExpressionInspectorTests.cs | 11 +++++++++++ .../Internal/ExpressionInspector.cs | 3 ++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/TestStack.FluentMVCTesting.Tests/Internal/ExpressionInspectorTests.cs b/TestStack.FluentMVCTesting.Tests/Internal/ExpressionInspectorTests.cs index 8abf890..363c209 100644 --- a/TestStack.FluentMVCTesting.Tests/Internal/ExpressionInspectorTests.cs +++ b/TestStack.FluentMVCTesting.Tests/Internal/ExpressionInspectorTests.cs @@ -83,6 +83,17 @@ public void Correctly_parse_conditional_or_operator() Assert.AreEqual("text => ((text == \"any\") || (text.Length == 3))", actual); } + + [Test] + public void Correctly_parse_conditional_and_operator() + { + Expression> func = + text => text == "any" && text.Length == 3; + ExpressionInspector sut = new ExpressionInspector(); + var actual = sut.Inspect(func); + Assert.AreEqual("text => ((text == \"any\") && (text.Length == 3))", actual); + } + [Test] public void Correctly_parse_two_conditional_or_operators() { diff --git a/TestStack.FluentMvcTesting/Internal/ExpressionInspector.cs b/TestStack.FluentMvcTesting/Internal/ExpressionInspector.cs index 5c2f30f..07c3299 100644 --- a/TestStack.FluentMvcTesting/Internal/ExpressionInspector.cs +++ b/TestStack.FluentMvcTesting/Internal/ExpressionInspector.cs @@ -8,7 +8,8 @@ internal class ExpressionInspector internal string Inspect(LambdaExpression expression) { return expression.ToString() - .Replace(" OrElse ", " || "); + .Replace(" OrElse ", " || ") + .Replace(" AndAlso ", " && "); } } } \ No newline at end of file From 1bfb9a38c3cfc55bddf9d1a5eefc939c1bdd5a7c Mon Sep 17 00:00:00 2001 From: ByteBlast Date: Sat, 18 Oct 2014 10:01:45 +0100 Subject: [PATCH 12/17] Support for parsing logical operators. --- .../Internal/ExpressionInspectorTests.cs | 28 +++++++++++++++++-- .../Internal/ExpressionInspector.cs | 5 +++- 2 files changed, 29 insertions(+), 4 deletions(-) diff --git a/TestStack.FluentMVCTesting.Tests/Internal/ExpressionInspectorTests.cs b/TestStack.FluentMVCTesting.Tests/Internal/ExpressionInspectorTests.cs index 363c209..b5e2c43 100644 --- a/TestStack.FluentMVCTesting.Tests/Internal/ExpressionInspectorTests.cs +++ b/TestStack.FluentMVCTesting.Tests/Internal/ExpressionInspectorTests.cs @@ -83,6 +83,16 @@ public void Correctly_parse_conditional_or_operator() Assert.AreEqual("text => ((text == \"any\") || (text.Length == 3))", actual); } + [Test] + public void Correctly_parse_two_conditional_or_operators() + { + Expression> func = + text => text == "any" || text.Length == 3 || text.Length == 9; + ExpressionInspector sut = new ExpressionInspector(); + var actual = sut.Inspect(func); + Assert.AreEqual("text => (((text == \"any\") || (text.Length == 3)) || (text.Length == 9))", actual); + } + [Test] public void Correctly_parse_conditional_and_operator() @@ -95,15 +105,27 @@ public void Correctly_parse_conditional_and_operator() } [Test] - public void Correctly_parse_two_conditional_or_operators() + public void Correctly_parse_logical_and_operator() { Expression> func = - text => text == "any" || text.Length == 3 || text.Length == 9; + text => text == "any" & text.Length == 3; ExpressionInspector sut = new ExpressionInspector(); var actual = sut.Inspect(func); - Assert.AreEqual("text => (((text == \"any\") || (text.Length == 3)) || (text.Length == 9))", actual); + Assert.AreEqual("text => ((text == \"any\") & (text.Length == 3))", actual); } + + [Test] + public void Correctly_parse_logical_or_operator() + { + Expression> func = + text => text == "any" | text.Length == 3; + ExpressionInspector sut = new ExpressionInspector(); + var actual = sut.Inspect(func); + Assert.AreEqual("text => ((text == \"any\") | (text.Length == 3))", actual); + } + + [Test] public void Not_mistake_property_called_OrElse_for_conditional_or_operator() { diff --git a/TestStack.FluentMvcTesting/Internal/ExpressionInspector.cs b/TestStack.FluentMvcTesting/Internal/ExpressionInspector.cs index 07c3299..9316576 100644 --- a/TestStack.FluentMvcTesting/Internal/ExpressionInspector.cs +++ b/TestStack.FluentMvcTesting/Internal/ExpressionInspector.cs @@ -9,7 +9,10 @@ internal string Inspect(LambdaExpression expression) { return expression.ToString() .Replace(" OrElse ", " || ") - .Replace(" AndAlso ", " && "); + .Replace(" AndAlso ", " && ") + .Replace(" Or ", " | ") + .Replace(" And ", " & "); + } } } \ No newline at end of file From 3c6e4ccefe123fb9f564e1cb9afa85d2d29b652e Mon Sep 17 00:00:00 2001 From: ByteBlast Date: Sat, 18 Oct 2014 10:04:25 +0100 Subject: [PATCH 13/17] Support for parsing null coalescing operator. --- .../Internal/ExpressionInspectorTests.cs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/TestStack.FluentMVCTesting.Tests/Internal/ExpressionInspectorTests.cs b/TestStack.FluentMVCTesting.Tests/Internal/ExpressionInspectorTests.cs index b5e2c43..85f0fca 100644 --- a/TestStack.FluentMVCTesting.Tests/Internal/ExpressionInspectorTests.cs +++ b/TestStack.FluentMVCTesting.Tests/Internal/ExpressionInspectorTests.cs @@ -125,6 +125,15 @@ public void Correctly_parse_logical_or_operator() Assert.AreEqual("text => ((text == \"any\") | (text.Length == 3))", actual); } + [Test] + public void Correctly_parse_null_coalescing_operator() + { + Expression> func = + text => text == ("" ?? "a"); + ExpressionInspector sut = new ExpressionInspector(); + var actual = sut.Inspect(func); + Assert.AreEqual("text => (text == (\"\" ?? \"a\"))", actual); + } [Test] public void Not_mistake_property_called_OrElse_for_conditional_or_operator() From 71e65640146c1851604597fb652d77afec1ee65e Mon Sep 17 00:00:00 2001 From: ByteBlast Date: Sat, 18 Oct 2014 10:07:50 +0100 Subject: [PATCH 14/17] Support for parsing logical xor operator. --- .../Internal/ExpressionInspectorTests.cs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/TestStack.FluentMVCTesting.Tests/Internal/ExpressionInspectorTests.cs b/TestStack.FluentMVCTesting.Tests/Internal/ExpressionInspectorTests.cs index 85f0fca..831a537 100644 --- a/TestStack.FluentMVCTesting.Tests/Internal/ExpressionInspectorTests.cs +++ b/TestStack.FluentMVCTesting.Tests/Internal/ExpressionInspectorTests.cs @@ -153,6 +153,16 @@ public void Correctly_parse_expression_whose_source_text_contains_parentheses() var actual = sut.Inspect(func); Assert.AreEqual("post => post.Title.Contains(\"\")", actual); } + + [Test] + public void Correctly_parse_logical_xor_operator() + { + Expression> func = + text => text == "any" ^ text.Length == 3; + ExpressionInspector sut = new ExpressionInspector(); + var actual = sut.Inspect(func); + Assert.AreEqual("text => ((text == \"any\") ^ (text.Length == 3))", actual); + } } public class PostViewModel From 587c6f718e4fb62363430a0452f1778d41ee4e8c Mon Sep 17 00:00:00 2001 From: ByteBlast Date: Sat, 18 Oct 2014 10:13:26 +0100 Subject: [PATCH 15/17] Readability tweaks. --- .../Internal/ExpressionInspectorTests.cs | 63 +++++++++---------- .../Internal/ExpressionInspector.cs | 2 - 2 files changed, 31 insertions(+), 34 deletions(-) diff --git a/TestStack.FluentMVCTesting.Tests/Internal/ExpressionInspectorTests.cs b/TestStack.FluentMVCTesting.Tests/Internal/ExpressionInspectorTests.cs index 831a537..a27155a 100644 --- a/TestStack.FluentMVCTesting.Tests/Internal/ExpressionInspectorTests.cs +++ b/TestStack.FluentMVCTesting.Tests/Internal/ExpressionInspectorTests.cs @@ -36,32 +36,32 @@ public void Correctly_parse_equality_comparison_with_property_operand() } [Test] - public void Correctly_parse_equality_comparison_with_property_operands() + public void Correctly_parse_equality_comparison_with_two_property_operands() { - Expression> func = post => post.Title == post.Slug; + Expression> func = post => + post.Title == post.Slug; ExpressionInspector sut = new ExpressionInspector(); var actual = sut.Inspect(func); Assert.AreEqual("post => (post.Title == post.Slug)", actual); } [Test] - public void Correctly_parse_inequality_comparison() + public void Correctly_parse_equality_comparison_with_captured_constant_operand() { - Expression> func = number => number != 5; + const int Number = 5; + Expression> func = number => number == Number; ExpressionInspector sut = new ExpressionInspector(); var actual = sut.Inspect(func); - Assert.AreEqual("number => (number != 5)", actual); + Assert.AreEqual(string.Concat("number => (number == ", Number, ")"), actual); } [Test] - public void Correctly_parse_equality_comparison_with_captured_constant_operand() + public void Correctly_parse_inequality_comparison() { - const int Number = 5; - Expression> func = number => number == Number; + Expression> func = number => number != 5; ExpressionInspector sut = new ExpressionInspector(); var actual = sut.Inspect(func); - Assert.AreEqual( - string.Concat("number => (number == ", Number, ")"), actual); + Assert.AreEqual("number => (number != 5)", actual); } [Test] @@ -73,6 +73,25 @@ public void Correctly_parse_relational_comparison() Assert.AreEqual("number => (number < 5)", actual); } + [Test] + public void Correctly_parse_expression_whose_source_text_contains_parentheses() + { + Expression> func = post => post.Title.Contains(""); + ExpressionInspector sut = new ExpressionInspector(); + var actual = sut.Inspect(func); + Assert.AreEqual("post => post.Title.Contains(\"\")", actual); + } + + [Test] + public void Correctly_parse_null_coalescing_operator() + { + Expression> func = + text => text == ("" ?? "a"); + ExpressionInspector sut = new ExpressionInspector(); + var actual = sut.Inspect(func); + Assert.AreEqual("text => (text == (\"\" ?? \"a\"))", actual); + } + [Test] public void Correctly_parse_conditional_or_operator() { @@ -90,10 +109,10 @@ public void Correctly_parse_two_conditional_or_operators() text => text == "any" || text.Length == 3 || text.Length == 9; ExpressionInspector sut = new ExpressionInspector(); var actual = sut.Inspect(func); - Assert.AreEqual("text => (((text == \"any\") || (text.Length == 3)) || (text.Length == 9))", actual); + Assert.AreEqual( + "text => (((text == \"any\") || (text.Length == 3)) || (text.Length == 9))", actual); } - [Test] public void Correctly_parse_conditional_and_operator() { @@ -114,7 +133,6 @@ public void Correctly_parse_logical_and_operator() Assert.AreEqual("text => ((text == \"any\") & (text.Length == 3))", actual); } - [Test] public void Correctly_parse_logical_or_operator() { @@ -125,16 +143,6 @@ public void Correctly_parse_logical_or_operator() Assert.AreEqual("text => ((text == \"any\") | (text.Length == 3))", actual); } - [Test] - public void Correctly_parse_null_coalescing_operator() - { - Expression> func = - text => text == ("" ?? "a"); - ExpressionInspector sut = new ExpressionInspector(); - var actual = sut.Inspect(func); - Assert.AreEqual("text => (text == (\"\" ?? \"a\"))", actual); - } - [Test] public void Not_mistake_property_called_OrElse_for_conditional_or_operator() { @@ -145,15 +153,6 @@ public void Not_mistake_property_called_OrElse_for_conditional_or_operator() Assert.AreEqual("post => ((post.Title == \"\") || (post.OrElse == \"\"))", actual); } - [Test] - public void Correctly_parse_expression_whose_source_text_contains_parentheses() - { - Expression> func = post => post.Title.Contains(""); - ExpressionInspector sut = new ExpressionInspector(); - var actual = sut.Inspect(func); - Assert.AreEqual("post => post.Title.Contains(\"\")", actual); - } - [Test] public void Correctly_parse_logical_xor_operator() { diff --git a/TestStack.FluentMvcTesting/Internal/ExpressionInspector.cs b/TestStack.FluentMvcTesting/Internal/ExpressionInspector.cs index 9316576..4637792 100644 --- a/TestStack.FluentMvcTesting/Internal/ExpressionInspector.cs +++ b/TestStack.FluentMvcTesting/Internal/ExpressionInspector.cs @@ -1,5 +1,4 @@ using System.Linq.Expressions; -using System.Text.RegularExpressions; namespace TestStack.FluentMVCTesting.Internal { @@ -12,7 +11,6 @@ internal string Inspect(LambdaExpression expression) .Replace(" AndAlso ", " && ") .Replace(" Or ", " | ") .Replace(" And ", " & "); - } } } \ No newline at end of file From a5a473f1bf98fb701d1a912631084e10d0bcc73a Mon Sep 17 00:00:00 2001 From: ByteBlast Date: Sat, 18 Oct 2014 10:39:41 +0100 Subject: [PATCH 16/17] Hooked up the ExpressionInspector. --- .../ViewResultTestTests.cs | 18 +++++++++--------- TestStack.FluentMvcTesting/ViewResultTest.cs | 4 +++- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/TestStack.FluentMVCTesting.Tests/ViewResultTestTests.cs b/TestStack.FluentMVCTesting.Tests/ViewResultTestTests.cs index 87647c5..9da2a2c 100644 --- a/TestStack.FluentMVCTesting.Tests/ViewResultTestTests.cs +++ b/TestStack.FluentMVCTesting.Tests/ViewResultTestTests.cs @@ -72,26 +72,26 @@ public void Check_for_invalid_model_using_predicate() var exception = Assert.Throws(() => _viewResultTest.WithModel(m => m.Property1 == null) ); - Assert.That(exception.Message, Is.EqualTo(string.Format("Expected view model {{\"Property1\":\"{0}\",\"Property2\":{1}}} to pass the given condition (m => m.Property1 == null), but it failed.", _model.Property1, _model.Property2))); + Assert.That(exception.Message, Is.EqualTo(string.Format("Expected view model {{\"Property1\":\"{0}\",\"Property2\":{1}}} to pass the given condition (m => (m.Property1 == null)), but it failed.", _model.Property1, _model.Property2))); } [Test] - public void Check_for_invalid_model_using_predicate_with_two_conditions() + public void Check_for_invalid_model_using_predicate_with_conditional_or() { var exception = Assert.Throws(() => - _viewResultTest.WithModel(m => m.Property1 == null && m.Property2 == 2) + _viewResultTest.WithModel(m => m.Property1 == null || m.Property2 == 1) ); - Assert.That(exception.Message, Is.EqualTo(string.Format("Expected view model {{\"Property1\":\"{0}\",\"Property2\":{1}}} to pass the given condition (m => m.Property1 == null && m.Property2 == 2), but it failed.", _model.Property1, _model.Property2))); + Assert.That(exception.Message, Is.EqualTo(string.Format("Expected view model {{\"Property1\":\"{0}\",\"Property2\":{1}}} to pass the given condition (m => ((m.Property1 == null) || (m.Property2 == 1))), but it failed.", _model.Property1, _model.Property2))); } [Test] - public void Check_for_invalid_model_using_predicate_with_closure() + public void Check_for_invalid_model_using_predicate_with_primitive_operand() { - int capturedOuterVariable = 2; + _viewResult.ViewData.Model = "abc"; var exception = Assert.Throws(() => - _viewResultTest.WithModel(m => m.Property1 == null && m.Property2 == capturedOuterVariable) + _viewResultTest.WithModel(m => m == "ab") ); - Assert.That(exception.Message, Is.EqualTo(string.Format("Expected view model {{\"Property1\":\"{0}\",\"Property2\":{1}}} to pass the given condition (m => m.Property1 == null AndAlso m.Property2 == 2), but it failed.", _model.Property1, capturedOuterVariable))); + Assert.That(exception.Message, Is.EqualTo(string.Format("Expected view model \"{0}\" to pass the given condition (m => (m == \"ab\")), but it failed.", _viewResult.ViewData.Model))); } [Test] @@ -114,7 +114,7 @@ public void Run_any_assertions_on_the_model() } } - class InvalidViewModel {} + class InvalidViewModel { } public class TestViewModel { public string Property1 { get; set; } diff --git a/TestStack.FluentMvcTesting/ViewResultTest.cs b/TestStack.FluentMvcTesting/ViewResultTest.cs index aefb1d7..ab52506 100644 --- a/TestStack.FluentMvcTesting/ViewResultTest.cs +++ b/TestStack.FluentMvcTesting/ViewResultTest.cs @@ -3,6 +3,7 @@ using System.Text.RegularExpressions; using System.Web.Helpers; using System.Web.Mvc; +using TestStack.FluentMVCTesting.Internal; namespace TestStack.FluentMVCTesting { @@ -46,8 +47,9 @@ public ModelTest WithModel(Expression> predic var model = _viewResult.Model as TModel; + var inspector = new ExpressionInspector(); var modelLex = Json.Encode(model); - var predicateLex = Regex.Replace(predicate.ToString(), "[()]", ""); + var predicateLex = inspector.Inspect(predicate); var compiledPredicate = predicate.Compile(); if (!compiledPredicate(model)) From 10cee9bc0d28874786029d18a8ebae366175794e Mon Sep 17 00:00:00 2001 From: ByteBlast Date: Sun, 26 Oct 2014 11:57:54 +0000 Subject: [PATCH 17/17] Resolved InternalsVisibleTo attribute discrepancy. --- TestStack.FluentMvcTesting/Properties/AssemblyInfo.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/TestStack.FluentMvcTesting/Properties/AssemblyInfo.cs b/TestStack.FluentMvcTesting/Properties/AssemblyInfo.cs index a4b5e0a..7fe02d7 100644 --- a/TestStack.FluentMvcTesting/Properties/AssemblyInfo.cs +++ b/TestStack.FluentMvcTesting/Properties/AssemblyInfo.cs @@ -35,3 +35,5 @@ [assembly: AssemblyVersion("1.0.0.0")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: InternalsVisibleTo("TestStack.FluentMVCTesting.Tests")] +[assembly: InternalsVisibleTo("TestStack.FluentMVCTesting.Mvc3.Tests")] +[assembly: InternalsVisibleTo("TestStack.FluentMVCTesting.Mvc4.Tests")]