From 7505d88b3c3c0e2e349f89378ecd9eeb210123a7 Mon Sep 17 00:00:00 2001 From: Rik Crompton Date: Thu, 21 Jun 2018 11:45:39 +0100 Subject: [PATCH 1/2] Added JsonResult Assertions --- .../ActionResultAssertions.cs | 29 +++ .../FailureMessages.Designer.cs | 149 +++++++----- .../FailureMessages.resx | 3 + .../FluentAssertions.Mvc.Shared.projitems | 1 + .../JsonResultAssertions.cs | 100 ++++++++ ...luentAssertions.Mvc.Tests.Shared.projitems | 1 + .../JsonResultAssertions_Tests.cs | 226 ++++++++++++++++++ 7 files changed, 454 insertions(+), 55 deletions(-) create mode 100644 src/FluentAssertions.Mvc.Shared/JsonResultAssertions.cs create mode 100644 tests/FluentAssertions.Mvc.Tests.Shared/JsonResultAssertions_Tests.cs diff --git a/src/FluentAssertions.Mvc.Shared/ActionResultAssertions.cs b/src/FluentAssertions.Mvc.Shared/ActionResultAssertions.cs index 333935b..d2eba97 100644 --- a/src/FluentAssertions.Mvc.Shared/ActionResultAssertions.cs +++ b/src/FluentAssertions.Mvc.Shared/ActionResultAssertions.cs @@ -209,5 +209,34 @@ public ViewResultAssertions BeViewResult(string reason, params object[] reasonAr return new ViewResultAssertions (Subject as ViewResult); } + + /// + /// Asserts that the subject is a . + /// + public JsonResultAssertions BeJsonResult() + { + return BeJsonResult(string.Empty, null); + } + + /// + /// Asserts that the subject is a . + /// + /// + /// A formatted phrase as is supported by explaining why the assertion + /// is needed. If the phrase does not start with the word because, it is prepended automatically. + /// + /// + /// Zero or more objects to format using the placeholders in . + /// + public JsonResultAssertions BeJsonResult(string reason, params object[] reasonArgs) + { + Execute.Assertion + .BecauseOf(reason, reasonArgs) + .ForCondition(Subject is JsonResult) + .FailWith(Constants.CommonFailMessage, typeof(JsonResult).Name, Subject.GetType().Name); + + return new JsonResultAssertions(Subject as JsonResult); + } + } } diff --git a/src/FluentAssertions.Mvc.Shared/FailureMessages.Designer.cs b/src/FluentAssertions.Mvc.Shared/FailureMessages.Designer.cs index a262f5f..fad448f 100644 --- a/src/FluentAssertions.Mvc.Shared/FailureMessages.Designer.cs +++ b/src/FluentAssertions.Mvc.Shared/FailureMessages.Designer.cs @@ -9,37 +9,43 @@ // //------------------------------------------------------------------------------ -namespace FluentAssertions.Mvc { +namespace FluentAssertions.Mvc +{ using System; using System.Reflection; -/// -/// A strongly-typed resource class, for looking up localized strings, etc. -/// -// This class was auto-generated by the StronglyTypedResourceBuilder -// class via a tool like ResGen or Visual Studio. -// To add or remove a member, edit your .ResX file then rerun ResGen -// with the /str option, or rebuild your VS project. -[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - internal class FailureMessages { - + internal class FailureMessages + { + private static global::System.Resources.ResourceManager resourceMan; - + private static global::System.Globalization.CultureInfo resourceCulture; - + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] - internal FailureMessages() { + internal FailureMessages() + { } - + /// /// Returns the cached ResourceManager instance used by this class. /// [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - internal static global::System.Resources.ResourceManager ResourceManager { - get { - if (object.ReferenceEquals(resourceMan, null)) { + internal static global::System.Resources.ResourceManager ResourceManager + { + get + { + if (object.ReferenceEquals(resourceMan, null)) + { global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("FluentAssertions.Mvc.FailureMessages", typeof(FailureMessages).Assembly); resourceMan = temp; @@ -47,119 +53,152 @@ internal FailureMessages() { return resourceMan; } } - + /// /// Overrides the current thread's CurrentUICulture property for all /// resource lookups using this strongly typed resource class. /// [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - internal static global::System.Globalization.CultureInfo Culture { - get { + internal static global::System.Globalization.CultureInfo Culture + { + get + { return resourceCulture; } - set { + set + { resourceCulture = value; } } - + /// /// Looks up a localized string similar to Expected {0} to be '{1}' but found '{2}'. /// - internal static string CommonFailMessage { - get { + internal static string CommonFailMessage + { + get + { return ResourceManager.GetString("CommonFailMessage", resourceCulture); } } - + /// /// Looks up a localized string similar to RouteData.DataTokens does not contain key {0}.. /// - internal static string RouteData_DataTokens_ContainsKey { - get { + internal static string RouteData_DataTokens_ContainsKey + { + get + { return ResourceManager.GetString("RouteData_DataTokens_ContainsKey", resourceCulture); } } - + /// /// Looks up a localized string similar to Expected RouteData.DataTokens[{0}] to have value {1}, but found {2}.. /// - internal static string RouteData_DataTokens_HaveValue { - get { + internal static string RouteData_DataTokens_HaveValue + { + get + { return ResourceManager.GetString("RouteData_DataTokens_HaveValue", resourceCulture); } } - + /// /// Looks up a localized string similar to RouteData.Values does not contain key {0}.. /// - internal static string RouteData_Values_ContainsKey { - get { + internal static string RouteData_Values_ContainsKey + { + get + { return ResourceManager.GetString("RouteData_Values_ContainsKey", resourceCulture); } } - + /// /// Looks up a localized string similar to Expected RouteData.Values[{0}] to have value {1}, but found {2}.. /// - internal static string RouteData_Values_HaveValue { - get { + internal static string RouteData_Values_HaveValue + { + get + { return ResourceManager.GetString("RouteData_Values_HaveValue", resourceCulture); } } - + /// /// Looks up a localized string similar to Expected MasterName to be {0} but found {1}.. /// - internal static string ViewResult_MasterName { - get { + internal static string ViewResult_MasterName + { + get + { return ResourceManager.GetString("ViewResult_MasterName", resourceCulture); } } - + /// /// Looks up a localized string similar to Expected Model to be of type {0}, but no Model was supplied.. /// - internal static string ViewResultBase_NullModel { - get { + internal static string ViewResultBase_NullModel + { + get + { return ResourceManager.GetString("ViewResultBase_NullModel", resourceCulture); } } - + /// /// Looks up a localized string similar to ViewData does not contain key of {0}.. /// - internal static string ViewResultBase_ViewData_ContainsKey { - get { + internal static string ViewResultBase_ViewData_ContainsKey + { + get + { return ResourceManager.GetString("ViewResultBase_ViewData_ContainsKey", resourceCulture); } } - + /// /// Looks up a localized string similar to Expected ViewData[{0}] to have value {1}, but found {2}.. /// - internal static string ViewResultBase_ViewData_HaveValue { - get { + internal static string ViewResultBase_ViewData_HaveValue + { + get + { return ResourceManager.GetString("ViewResultBase_ViewData_HaveValue", resourceCulture); } } - + /// /// Looks up a localized string similar to Expected ViewName to be {0} but found {1}.. /// - internal static string ViewResultBase_ViewName { - get { + internal static string ViewResultBase_ViewName + { + get + { return ResourceManager.GetString("ViewResultBase_ViewName", resourceCulture); } } - + /// /// Looks up a localized string similar to Expected default view, but view {0} was rendered.. /// - internal static string ViewResultBase_WithDefaultViewName { - get { + internal static string ViewResultBase_WithDefaultViewName + { + get + { return ResourceManager.GetString("ViewResultBase_WithDefaultViewName", resourceCulture); } } + + internal static string JsonResult_WithDataPredicate + { + get + { + return ResourceManager.GetString("JsonResult_WithDataPredicate", resourceCulture); + } + } } } #endif \ No newline at end of file diff --git a/src/FluentAssertions.Mvc.Shared/FailureMessages.resx b/src/FluentAssertions.Mvc.Shared/FailureMessages.resx index ac089de..7c8a2de 100644 --- a/src/FluentAssertions.Mvc.Shared/FailureMessages.resx +++ b/src/FluentAssertions.Mvc.Shared/FailureMessages.resx @@ -120,6 +120,9 @@ Expected {0} to be '{1}' but found '{2}' + + Expected {0} to match the predicate test, but '{1}' did not + RouteData.DataTokens does not contain key {0}. diff --git a/src/FluentAssertions.Mvc.Shared/FluentAssertions.Mvc.Shared.projitems b/src/FluentAssertions.Mvc.Shared/FluentAssertions.Mvc.Shared.projitems index 6fcd139..34a059e 100644 --- a/src/FluentAssertions.Mvc.Shared/FluentAssertions.Mvc.Shared.projitems +++ b/src/FluentAssertions.Mvc.Shared/FluentAssertions.Mvc.Shared.projitems @@ -22,6 +22,7 @@ + diff --git a/src/FluentAssertions.Mvc.Shared/JsonResultAssertions.cs b/src/FluentAssertions.Mvc.Shared/JsonResultAssertions.cs new file mode 100644 index 0000000..437a67d --- /dev/null +++ b/src/FluentAssertions.Mvc.Shared/JsonResultAssertions.cs @@ -0,0 +1,100 @@ +using System; +using System.Text; +using FluentAssertions.Execution; +using FluentAssertions.Primitives; +#if NETSTANDARD1_6 +using Microsoft.AspNetCore.Mvc; +#else +using System.Web.Mvc; +#endif + +namespace FluentAssertions.Mvc +{ + /// + /// Contains a number of methods to assert that a is in the expected state. + /// + public class JsonResultAssertions : ObjectAssertions + { + /// + /// Initializes a new instance of the class. + /// + public JsonResultAssertions(JsonResult subject) : base(subject) + { + + } + + /// + /// Asserts that the Data is exactly the same as the expected Data. + /// This uses a standard object.Equals comparisson so the validity of this test will depend on the nature of the equality overrides (if any) + /// + /// The expected content of the result. + /// + /// A formatted phrase as is supported by explaining why the assertion + /// is needed. If the phrase does not start with the word because, it is prepended automatically. + /// + /// + /// Zero or more objects to format using the placeholders in . + /// + public JsonResultAssertions WithData(object expectedData, string reason = "", params object[] reasonArgs) + { + var actual = (Subject as JsonResult).Data; + + Execute.Assertion + .ForCondition(object.Equals(actual, expectedData)) + .BecauseOf(reason, reasonArgs) + .FailWith(string.Format(FailureMessages.CommonFailMessage, "JsonResult.Data", expectedData, actual)); + + return this; + } + + /// + /// Asserts that the Data is exactly the same as the expected Data. + /// This uses a standard object.Equals comparisson so the validity of this test will depend on the nature of the equality overrides (if any) + /// + /// A predicate that validates the data. + /// + /// A formatted phrase as is supported by explaining why the assertion + /// is needed. If the phrase does not start with the word because, it is prepended automatically. + /// + /// + /// Zero or more objects to format using the placeholders in . + /// + public JsonResultAssertions WithData(Func dataCondition, string reason = "", params object[] reasonArgs) + { + var actual = (Subject as JsonResult).Data; + + Execute.Assertion + .ForCondition(dataCondition(actual)) + .BecauseOf(reason, reasonArgs) + .FailWith(string.Format(FailureMessages.JsonResult_WithDataPredicate, "JsonResult.Data", actual)); + + return this; + } + +#if !NETSTANDARD1_6 + /// + /// Asserts that the content encoding is the expected content encoding type. + /// + /// The expected content encoding type. + /// + /// A formatted phrase as is supported by explaining why the assertion + /// is needed. If the phrase does not start with the word because, it is prepended automatically. + /// + /// + /// Zero or more objects to format using the placeholders in . + /// + public JsonResultAssertions WithContentEncoding(Encoding expectedEncoding, string reason = "", params object[] reasonArgs) + { + Encoding actualContentEncoding = (Subject as JsonResult).ContentEncoding; + + Execute.Assertion + .ForCondition(expectedEncoding == actualContentEncoding) + .BecauseOf(reason, reasonArgs) + .FailWith(string.Format(FailureMessages.CommonFailMessage, "JsonResult.ContentEncoding", expectedEncoding.ToString(), actualContentEncoding.ToString())); + + return this; + } +#endif + + } +} \ No newline at end of file diff --git a/tests/FluentAssertions.Mvc.Tests.Shared/FluentAssertions.Mvc.Tests.Shared.projitems b/tests/FluentAssertions.Mvc.Tests.Shared/FluentAssertions.Mvc.Tests.Shared.projitems index 671df5a..ceb4889 100644 --- a/tests/FluentAssertions.Mvc.Tests.Shared/FluentAssertions.Mvc.Tests.Shared.projitems +++ b/tests/FluentAssertions.Mvc.Tests.Shared/FluentAssertions.Mvc.Tests.Shared.projitems @@ -14,6 +14,7 @@ + diff --git a/tests/FluentAssertions.Mvc.Tests.Shared/JsonResultAssertions_Tests.cs b/tests/FluentAssertions.Mvc.Tests.Shared/JsonResultAssertions_Tests.cs new file mode 100644 index 0000000..513756a --- /dev/null +++ b/tests/FluentAssertions.Mvc.Tests.Shared/JsonResultAssertions_Tests.cs @@ -0,0 +1,226 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using NUnit.Framework; +#if NETCOREAPP1_0 +using Microsoft.AspNetCore.Mvc; +#else +using System.Web.Mvc; +#endif + +namespace FluentAssertions.Mvc.Tests +{ + [TestFixture] + public class JsonResultAssertions_Tests + { + [Test] + public void WithJson_ShouldPass() + { + ActionResult result = new JsonResult { Data = new List() }; + result.Should().BeJsonResult(); + } + + [Test] + public void WithContentResult_ShouldFail() + { + ActionResult result = new ContentResult {Content = "{ \"name\" : \"WithJson_GivenExpected_ShouldPass\" }"}; + + // this is a bit of a hack to build the expected message. There must be a better way? + var messageFormat = ActionResultAssertions.Constants.CommonFailMessage + .Replace("{reason}","") + .Replace("{", "\"{") + .Replace("}", "}\""); + var failureMessage = String.Format(messageFormat, typeof(JsonResult).Name, result.GetType().Name); + + System.Action act = () => result.Should().BeJsonResult(); + + act.Should().Throw().WithMessage(failureMessage); + } + + #region WithData object + [Test] + public void WithData_DataIsSameObject() + { + var data = new int[] {10, 20, 30, 40}; + ActionResult result = new JsonResult { Data = data }; + + result.Should().BeJsonResult().WithData(data); + } + + [Test] + public void WithData_DataIsDifferentObject() + { + var actualData = new int[] {10, 20, 30, 40}; + var differentData = new string[] {"10", "20", "30", "40"}; + ActionResult result = new JsonResult { Data = actualData }; + var expectedMessage = string.Format(FailureMessages.CommonFailMessage, "JsonResult.Data", differentData, actualData); + + System.Action act = () => result.Should().BeJsonResult().WithData(differentData); + + act.Should().Throw().WithMessage(expectedMessage); + } + + [Test] + public void WithData_DataIsDifferentObjectOfSameValue() + { + var actualData = new ObjectWithEquality("hello world"); + var expectedData = new ObjectWithEquality("hello world"); + + ActionResult result = new JsonResult { Data = actualData }; + + result.Should().BeJsonResult().WithData(expectedData); + } + + [Test] + public void WithData_DataIsDifferentObjectOfDifferentValue() + { + var actualData = new ObjectWithEquality("hello world"); + var expectedData = new ObjectWithEquality("goodbye cruel world"); + var expectedMessage = string.Format(FailureMessages.CommonFailMessage, "JsonResult.Data", expectedData, actualData); + + ActionResult result = new JsonResult { Data = actualData }; + + System.Action act = () => result.Should().BeJsonResult().WithData(expectedData); + + act.Should().Throw().WithMessage(expectedMessage); + } + #endregion + + #region WithData predicate + + [Test] + public void WithDataPredicate_ShouldPass() + { + var data = new int[] {10, 20, 30, 40}; + ActionResult result = new JsonResult { Data = data }; + + result.Should().BeJsonResult().WithData(d => + { + var isGood = false; + + if (d is IEnumerable ids) + { + isGood = ids.Contains(10) && ids.Contains(20); + } + return isGood; + }); + } + + [Test] + public void WithDataPredicate_ShouldFail() + { + var actualData = new int[] { 10, 20, 30, 40 }; + ActionResult result = new JsonResult { Data = actualData }; + var expectedMessage = string.Format(FailureMessages.JsonResult_WithDataPredicate, "JsonResult.Data", actualData); + + System.Action act = () => result.Should() + .BeJsonResult() + .WithData(d => false); + + act.Should().Throw().WithMessage(expectedMessage); + } + + //[Test] + //public void WithData_DataIsDifferentObjectOfSameValue() + //{ + // var actualData = new ObjectWithEquality("hello world"); + // var expectedData = new ObjectWithEquality("hello world"); + + // ActionResult result = new JsonResult { Data = actualData }; + + // result.Should().BeJsonResult().WithData(expectedData); + //} + + //[Test] + //public void WithData_DataIsDifferentObjectOfDifferentValue() + //{ + // var actualData = new ObjectWithEquality("hello world"); + // var expectedData = new ObjectWithEquality("goodbye cruel world"); + // var expectedMessage = string.Format(FailureMessages.CommonFailMessage, "JsonResult.Data", expectedData, actualData); + + // ActionResult result = new JsonResult { Data = actualData }; + + // System.Action act = () => result.Should().BeJsonResult().WithData(expectedData); + + // act.Should().Throw().WithMessage(expectedMessage); + //} + + #endregion + +#if !NETCOREAPP1_0 + [Test] + public void WithContentEncoding_GivenExpected_ShouldPass() + { + ActionResult result = new JsonResult { ContentEncoding = Encoding.ASCII }; + result.Should().BeJsonResult().WithContentEncoding(Encoding.ASCII); + } + + [Test] + public void WithContentEncoding_GivenUnexpected_ShouldFail() + { + var actualEncoding = Encoding.ASCII; + var expectedEncoding = Encoding.Unicode; + ActionResult result = new JsonResult { ContentEncoding = actualEncoding }; + var failureMessage = String.Format(FailureMessages.CommonFailMessage, "JsonResult.ContentEncoding", expectedEncoding, actualEncoding); + + Action a = () => result.Should().BeJsonResult().WithContentEncoding(expectedEncoding); + + a.Should().Throw() + .WithMessage(failureMessage); + } +#endif + + private class ObjectWithEquality : IEquatable + { + private readonly string _value; + + public ObjectWithEquality(string value) + { + _value = value; + } + + public string Value => _value; + + #region Equality + + public bool Equals(ObjectWithEquality other) + { + if (ReferenceEquals(null, other)) return false; + if (ReferenceEquals(this, other)) return true; + return string.Equals(Value, other.Value, StringComparison.CurrentCultureIgnoreCase); + } + + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) return false; + if (ReferenceEquals(this, obj)) return true; + if (obj.GetType() != this.GetType()) return false; + return Equals((ObjectWithEquality) obj); + } + + public override int GetHashCode() + { + return (Value != null ? StringComparer.CurrentCultureIgnoreCase.GetHashCode(Value) : 0); + } + + public static bool operator ==(ObjectWithEquality left, ObjectWithEquality right) + { + return Equals(left, right); + } + + public static bool operator !=(ObjectWithEquality left, ObjectWithEquality right) + { + return !Equals(left, right); + } + + #endregion + + public override string ToString() + { + return _value ?? "NULL"; + } + } + + } +} \ No newline at end of file From 80333a9887bc7c3255fd6ca32faadbf7c789cd99 Mon Sep 17 00:00:00 2001 From: Rik Crompton Date: Thu, 21 Jun 2018 16:43:56 +0100 Subject: [PATCH 2/2] Removed commented out code and unnecessary compiler directives --- .../JsonResultAssertions.cs | 7 ----- .../JsonResultAssertions_Tests.cs | 31 ------------------- 2 files changed, 38 deletions(-) diff --git a/src/FluentAssertions.Mvc.Shared/JsonResultAssertions.cs b/src/FluentAssertions.Mvc.Shared/JsonResultAssertions.cs index 437a67d..77387cd 100644 --- a/src/FluentAssertions.Mvc.Shared/JsonResultAssertions.cs +++ b/src/FluentAssertions.Mvc.Shared/JsonResultAssertions.cs @@ -2,11 +2,7 @@ using System.Text; using FluentAssertions.Execution; using FluentAssertions.Primitives; -#if NETSTANDARD1_6 -using Microsoft.AspNetCore.Mvc; -#else using System.Web.Mvc; -#endif namespace FluentAssertions.Mvc { @@ -71,7 +67,6 @@ public JsonResultAssertions WithData(Func dataCondition, string re return this; } -#if !NETSTANDARD1_6 /// /// Asserts that the content encoding is the expected content encoding type. /// @@ -94,7 +89,5 @@ public JsonResultAssertions WithContentEncoding(Encoding expectedEncoding, strin return this; } -#endif - } } \ No newline at end of file diff --git a/tests/FluentAssertions.Mvc.Tests.Shared/JsonResultAssertions_Tests.cs b/tests/FluentAssertions.Mvc.Tests.Shared/JsonResultAssertions_Tests.cs index 513756a..577d3ef 100644 --- a/tests/FluentAssertions.Mvc.Tests.Shared/JsonResultAssertions_Tests.cs +++ b/tests/FluentAssertions.Mvc.Tests.Shared/JsonResultAssertions_Tests.cs @@ -3,11 +3,7 @@ using System.Linq; using System.Text; using NUnit.Framework; -#if NETCOREAPP1_0 -using Microsoft.AspNetCore.Mvc; -#else using System.Web.Mvc; -#endif namespace FluentAssertions.Mvc.Tests { @@ -121,34 +117,8 @@ public void WithDataPredicate_ShouldFail() act.Should().Throw().WithMessage(expectedMessage); } - //[Test] - //public void WithData_DataIsDifferentObjectOfSameValue() - //{ - // var actualData = new ObjectWithEquality("hello world"); - // var expectedData = new ObjectWithEquality("hello world"); - - // ActionResult result = new JsonResult { Data = actualData }; - - // result.Should().BeJsonResult().WithData(expectedData); - //} - - //[Test] - //public void WithData_DataIsDifferentObjectOfDifferentValue() - //{ - // var actualData = new ObjectWithEquality("hello world"); - // var expectedData = new ObjectWithEquality("goodbye cruel world"); - // var expectedMessage = string.Format(FailureMessages.CommonFailMessage, "JsonResult.Data", expectedData, actualData); - - // ActionResult result = new JsonResult { Data = actualData }; - - // System.Action act = () => result.Should().BeJsonResult().WithData(expectedData); - - // act.Should().Throw().WithMessage(expectedMessage); - //} - #endregion -#if !NETCOREAPP1_0 [Test] public void WithContentEncoding_GivenExpected_ShouldPass() { @@ -169,7 +139,6 @@ public void WithContentEncoding_GivenUnexpected_ShouldFail() a.Should().Throw() .WithMessage(failureMessage); } -#endif private class ObjectWithEquality : IEquatable {