From c0997a9d47de90b30f6a77201f3bdd15068ceae8 Mon Sep 17 00:00:00 2001 From: Viktor Faddi Date: Sun, 1 Nov 2020 10:29:19 +0100 Subject: [PATCH 1/4] Fix typo. --- src/FluentAssertions.AspNetCore.Mvc/ActionResultAssertions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/FluentAssertions.AspNetCore.Mvc/ActionResultAssertions.cs b/src/FluentAssertions.AspNetCore.Mvc/ActionResultAssertions.cs index 47466f1..f9a589d 100644 --- a/src/FluentAssertions.AspNetCore.Mvc/ActionResultAssertions.cs +++ b/src/FluentAssertions.AspNetCore.Mvc/ActionResultAssertions.cs @@ -139,7 +139,7 @@ public FileStreamResultAssertions BeFileStreamResult(string reason = "", params } /// - /// Asserts that the subject is an . + /// Asserts that the subject is an . /// /// /// A formatted phrase as is supported by explaining why the assertion From 868a26d309ecabc9df6f8e36aee864328aaf391f Mon Sep 17 00:00:00 2001 From: Viktor Faddi Date: Sun, 1 Nov 2020 10:30:18 +0100 Subject: [PATCH 2/4] Add BeObjectResult to ActionResultAssertions. --- .../ProductController_Tests.cs | 4 ++-- .../ActionResultAssertionsOfTValue.cs | 21 +++++++++++++++++- .../ActionResultAssertionsOfTValue_Tests.cs | 22 +++++++++++++++++++ 3 files changed, 44 insertions(+), 3 deletions(-) diff --git a/samples/FluentAssertions.AspNetCore.Mvc.Sample.Tests/ProductController_Tests.cs b/samples/FluentAssertions.AspNetCore.Mvc.Sample.Tests/ProductController_Tests.cs index f0c984f..06d5bfb 100644 --- a/samples/FluentAssertions.AspNetCore.Mvc.Sample.Tests/ProductController_Tests.cs +++ b/samples/FluentAssertions.AspNetCore.Mvc.Sample.Tests/ProductController_Tests.cs @@ -25,8 +25,8 @@ public void GetActionResultOfT_OnFalse_Returns_Data() var result = controller.GetActionResultOfT(model, returnError); - result.Should().BeConvertibleTo() - .And.Value.Should().BeSameAs(model); + result.Should().BeObjectResult() + .Value.Should().Be(model); } [Fact] diff --git a/src/FluentAssertions.AspNetCore.Mvc/ActionResultAssertionsOfTValue.cs b/src/FluentAssertions.AspNetCore.Mvc/ActionResultAssertionsOfTValue.cs index 5582e44..0b6e610 100644 --- a/src/FluentAssertions.AspNetCore.Mvc/ActionResultAssertionsOfTValue.cs +++ b/src/FluentAssertions.AspNetCore.Mvc/ActionResultAssertionsOfTValue.cs @@ -79,6 +79,25 @@ public AndWhichConstraint, TActionResult> BeConve return new AndWhichConstraint, TActionResult>(this, (TActionResult)convertResult); } + + /// + /// Asserts that the is type of . + /// + /// + /// 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 . + /// + [CustomAssertion] + public ObjectResultAssertions BeObjectResult(string reason = "", params object[] reasonArgs) + { + var result = BeConvertibleTo(reason, reasonArgs).Which; + + return new ObjectResultAssertions(result); + } + + #endregion Public Methods } - #endregion Public Methods } diff --git a/tests/FluentAssertions.AspNetCore.Mvc.Tests/ActionResultAssertionsOfTValue_Tests.cs b/tests/FluentAssertions.AspNetCore.Mvc.Tests/ActionResultAssertionsOfTValue_Tests.cs index 0614f45..6604858 100644 --- a/tests/FluentAssertions.AspNetCore.Mvc.Tests/ActionResultAssertionsOfTValue_Tests.cs +++ b/tests/FluentAssertions.AspNetCore.Mvc.Tests/ActionResultAssertionsOfTValue_Tests.cs @@ -72,6 +72,28 @@ public void BeConvertibleTo_ShouldBeTheConvertedObject() actual.Should().BeSameAs(expectation); } + + [Fact] + public void BeObjectResult_GivenActionResultWithObjectResult_ShouldPass() + { + var result = new ActionResult(new object()); + + result.Should().BeObjectResult(Reason, ReasonArgs); + } + + [Fact] + public void BeObjectResult_GivenActionResultWithNotObjectResult_ShouldFail() + { + var result = new ActionResult(new BadRequestObjectResult(new object())); + var failureMessage = FailureMessageHelper.ExpectedContextToBeConvertible( + "result", typeof(ObjectResult).FullName, typeof(BadRequestObjectResult).FullName); + + Action action = () => result.Should().BeObjectResult(Reason, ReasonArgs); + + action.Should().Throw() + .WithMessage(failureMessage); + } + #endregion Public Methods } } \ No newline at end of file From 21a6c3b204d35a3fe90699b530a6a5a2d300aefd Mon Sep 17 00:00:00 2001 From: Viktor Faddi Date: Sun, 1 Nov 2020 12:17:24 +0100 Subject: [PATCH 3/4] Add WithValue, WithValueEquivalentTo, WithValueMatch checks to ObjectResult. --- .../ProductController_Tests.cs | 5 +- .../ObjectResultAssertionsBase.cs | 101 +++++++++++++++++- .../ActionResultAssertionsOfTValue_Tests.cs | 1 - .../ObjectResultAssertions_Tests.cs | 81 ++++++++++++++ 4 files changed, 182 insertions(+), 6 deletions(-) diff --git a/samples/FluentAssertions.AspNetCore.Mvc.Sample.Tests/ProductController_Tests.cs b/samples/FluentAssertions.AspNetCore.Mvc.Sample.Tests/ProductController_Tests.cs index 06d5bfb..2f7450f 100644 --- a/samples/FluentAssertions.AspNetCore.Mvc.Sample.Tests/ProductController_Tests.cs +++ b/samples/FluentAssertions.AspNetCore.Mvc.Sample.Tests/ProductController_Tests.cs @@ -1,6 +1,5 @@ using FluentAssertions.AspNetCore.Mvc.Sample.Controllers; using Microsoft.AspNetCore.Mvc; -using System; using Xunit; namespace FluentAssertions.AspNetCore.Mvc.Sample.Tests @@ -26,7 +25,9 @@ public void GetActionResultOfT_OnFalse_Returns_Data() var result = controller.GetActionResultOfT(model, returnError); result.Should().BeObjectResult() - .Value.Should().Be(model); + .WithValue(model) // Equals check + .WithValueEquivalentTo(model) // Equivalency check + .WithValueMatch(m => m.Id == 1); // match check. } [Fact] diff --git a/src/FluentAssertions.AspNetCore.Mvc/ObjectResultAssertionsBase.cs b/src/FluentAssertions.AspNetCore.Mvc/ObjectResultAssertionsBase.cs index 25a76a4..d6d18ac 100644 --- a/src/FluentAssertions.AspNetCore.Mvc/ObjectResultAssertionsBase.cs +++ b/src/FluentAssertions.AspNetCore.Mvc/ObjectResultAssertionsBase.cs @@ -1,4 +1,6 @@ -using FluentAssertions.Execution; +using FluentAssertions.Common; +using FluentAssertions.Equivalency; +using FluentAssertions.Execution; using FluentAssertions.Primitives; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Formatters; @@ -157,14 +159,107 @@ public TObjectResultAssertion WithDeclaredType(Type expectedDeclaredType, string var actual = ObjectResultSubject.DeclaredType; Execute.Assertion + .BecauseOf(reason, reasonArgs) .ForCondition(expectedDeclaredType == actual) .WithDefaultIdentifier(Identifier + ".DeclaredType") - .BecauseOf(reason, reasonArgs) .FailWith(FailureMessages.CommonTypeFailMessage, expectedDeclaredType, actual); return (TObjectResultAssertion)this; } + /// + /// Asserts that the is the expected value. + /// + /// The expected value. + /// + /// 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 TObjectResultAssertion WithValue(object expectedValue, string reason = "", params object[] reasonArgs) + { + object actualValue = ObjectResultSubject.Value; + + Execute.Assertion + .BecauseOf(reason, reasonArgs) + .ForCondition(actualValue.IsSameOrEqualTo(expectedValue)) + .WithDefaultIdentifier(Identifier + ".Value") + .FailWith(FailureMessages.CommonFailMessage, expectedValue, actualValue); + + return (TObjectResultAssertion)this; + } + + /// + /// Asserts that the is equivalent to another object. + /// + /// The expected value. + /// + /// 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 TObjectResultAssertion WithValueEquivalentTo(TExpectation expectation, + string reason = "", params object[] reasonArgs) + { + return WithValueEquivalentTo(expectation, config => config, reason, reasonArgs); + } + + /// + /// Asserts that the is equivalent to another object. + /// + /// The expected status code. + /// + /// A reference to the configuration object that can be used + /// to influence the way the object graphs are compared. You can also provide an alternative instance of the + /// class. The global defaults are determined by the + /// class. + /// + /// + /// 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 TObjectResultAssertion WithValueEquivalentTo(TExpectation expectation, + Func, EquivalencyAssertionOptions> config, string reason = "", params object[] reasonArgs) + { + object actualValue = ObjectResultSubject.Value; + + actualValue.Should().BeEquivalentTo(expectation, config, reason, reasonArgs); + + return (TObjectResultAssertion)this; + } + + + /// + /// Asserts that the statisfies the . + /// + /// + /// The predicate which must be satisfied by the . + /// + /// 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 TObjectResultAssertion WithValueMatch(Expression> predicate, + string reason = "", params object[] reasonArgs) + { + object actualValue = ValueAs(); + + using(var scope = new AssertionScope(Identifier + ".Value")) + { + actualValue.Should().Match(predicate, reason, reasonArgs); + } + + return (TObjectResultAssertion)this; + } /// /// Asserts that the is the expected status code. @@ -182,9 +277,9 @@ public TObjectResultAssertion WithStatusCode(int? expectedStatusCode, string rea var actual = ObjectResultSubject.StatusCode; Execute.Assertion + .BecauseOf(reason, reasonArgs) .ForCondition(expectedStatusCode == actual) .WithDefaultIdentifier(Identifier + ".StatusCode") - .BecauseOf(reason, reasonArgs) .FailWith(FailureMessages.CommonFailMessage, expectedStatusCode, actual); return (TObjectResultAssertion)this; diff --git a/tests/FluentAssertions.AspNetCore.Mvc.Tests/ActionResultAssertionsOfTValue_Tests.cs b/tests/FluentAssertions.AspNetCore.Mvc.Tests/ActionResultAssertionsOfTValue_Tests.cs index 6604858..b092c79 100644 --- a/tests/FluentAssertions.AspNetCore.Mvc.Tests/ActionResultAssertionsOfTValue_Tests.cs +++ b/tests/FluentAssertions.AspNetCore.Mvc.Tests/ActionResultAssertionsOfTValue_Tests.cs @@ -1,6 +1,5 @@ using FluentAssertions.Mvc.Tests.Helpers; using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Mvc.Infrastructure; using System; using Xunit; diff --git a/tests/FluentAssertions.AspNetCore.Mvc.Tests/ObjectResultAssertions_Tests.cs b/tests/FluentAssertions.AspNetCore.Mvc.Tests/ObjectResultAssertions_Tests.cs index 19df4e6..b357564 100644 --- a/tests/FluentAssertions.AspNetCore.Mvc.Tests/ObjectResultAssertions_Tests.cs +++ b/tests/FluentAssertions.AspNetCore.Mvc.Tests/ObjectResultAssertions_Tests.cs @@ -12,6 +12,11 @@ namespace FluentAssertions.AspNetCore.Mvc.Tests public class ObjectResultAssertions_Tests { private const string TestValue = "testValue"; + private const string WrongValue = "wrongValue"; + + private readonly object TestObject = new { Value = "testValue" }; + private readonly object GoodObject = new { Value = "testValue" }; + private readonly object WrongObject = new { Value = "wrongValue" }; public const string Reason = FailureMessageHelper.Reason; public readonly static object[] ReasonArgs = FailureMessageHelper.ReasonArgs; @@ -219,5 +224,81 @@ public void WithStatusCode_GivenUnexpected_ShouldFail() a.Should().Throw() .WithMessage(failureMessage); } + + [Fact] + public void WithValue_GivenExpected_ShouldPass() + { + var result = new ObjectResult(TestValue); + + result.Should().BeObjectResult() + .WithValue(TestValue); + } + + [Fact] + public void WithValue_GivenUnexpected_ShouldFail() + { + var result = new ObjectResult(WrongValue); + string failureMessage = FailureMessageHelper.ExpectedContextToBeXButY( + "ObjectResult.Value", + TestValue, + WrongValue); + + Action a = () => result.Should().BeObjectResult().WithValue(TestValue, Reason, ReasonArgs); + + a.Should().Throw() + .WithMessage(failureMessage); + } + + [Fact] + public void WithValueEquivalentTo_GivenExpected_ShouldPass() + { + var result = new ObjectResult(TestObject); + + result.Should().BeObjectResult() + .WithValueEquivalentTo(GoodObject); + } + + [Fact] + public void WithValueEquivalentTo_GivenUnexpected_ShouldFail() + { + var result = new ObjectResult(WrongObject); + string failureMessage = @"Expected member Value to be +""testValue"" with a length of 9 because it is 10, but +""wrongValue"" has a length of 10. + +With configuration: +- Use declared types and members +- Compare enums by value +- Match member by name (or throw) +- Without automatic conversion. +- Be strict about the order of items in byte arrays"; + + Action a = () => result.Should().BeObjectResult().WithValueEquivalentTo(GoodObject, Reason, ReasonArgs); + + a.Should().Throw() + .WithMessage(failureMessage); + } + + [Fact] + public void WithValueMatch_GivenExpected_ShouldPass() + { + var result = new ObjectResult(TestValue); + + result.Should().BeObjectResult() + .WithValueMatch(value => value == TestValue); + } + + [Fact] + public void WithValueMatch_GivenUnexpected_ShouldFail() + { + var result = new ObjectResult(WrongValue); + string failureMessage = "Expected ObjectResult.Value to match (value == \"testValue\") because it is 10, but found \"wrongValue\"."; + + Action a = () => result.Should().BeObjectResult().WithValueMatch(value => value == TestValue, Reason, ReasonArgs); + + a.Should().Throw() + .WithMessage(failureMessage); + } + } } From 3f7d785b978472d302e4bd3541223a1f3f3c52ce Mon Sep 17 00:00:00 2001 From: Viktor Faddi Date: Sun, 1 Nov 2020 12:24:19 +0100 Subject: [PATCH 4/4] Bunp version. Fix comment. --- .../FluentAssertions.AspNetCore.Mvc.csproj | 2 +- .../ObjectResultAssertionsBase.cs | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/FluentAssertions.AspNetCore.Mvc/FluentAssertions.AspNetCore.Mvc.csproj b/src/FluentAssertions.AspNetCore.Mvc/FluentAssertions.AspNetCore.Mvc.csproj index 4222a96..4899ad1 100644 --- a/src/FluentAssertions.AspNetCore.Mvc/FluentAssertions.AspNetCore.Mvc.csproj +++ b/src/FluentAssertions.AspNetCore.Mvc/FluentAssertions.AspNetCore.Mvc.csproj @@ -5,7 +5,7 @@ Copyright 2018 Fluent Assertions extensions for ASP.NET Core MVC Fluent Assertions for ASP.NET Core MVC - 3.1.0 + 3.2.0 Casey Burns;Kevin Kuszyk netstandard2.0;netcoreapp3.0 FluentAssertions.AspNetCore.Mvc diff --git a/src/FluentAssertions.AspNetCore.Mvc/ObjectResultAssertionsBase.cs b/src/FluentAssertions.AspNetCore.Mvc/ObjectResultAssertionsBase.cs index d6d18ac..15a2048 100644 --- a/src/FluentAssertions.AspNetCore.Mvc/ObjectResultAssertionsBase.cs +++ b/src/FluentAssertions.AspNetCore.Mvc/ObjectResultAssertionsBase.cs @@ -241,6 +241,7 @@ public TObjectResultAssertion WithValueEquivalentTo(TExpectation e /// /// /// The predicate which must be satisfied by the . + /// /// /// 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.