Skip to content

Commit 7e7749e

Browse files
committed
Finishing the implementation of ConvertToActionAssertions and ActionResultAssertionsOfTValue.
1 parent 65fb6e1 commit 7e7749e

File tree

8 files changed

+194
-31
lines changed

8 files changed

+194
-31
lines changed

src/FluentAssertions.AspNetCore.Mvc/ActionResultAssertionsOfTValue.cs

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
#if NETCOREAPP3_0
21
using FluentAssertions.Execution;
32
using FluentAssertions.Primitives;
43
using Microsoft.AspNetCore.Mvc;
@@ -47,9 +46,32 @@ public ActionResultAssertions(ActionResult<TValue> subject)
4746

4847
#region Public Methods
4948

50-
/// TODO What should be here?
51-
52-
#endregion Public Methods
49+
/// <summary>
50+
/// Asserts that the <see cref="ActionResult{TValue}.Result"/> is type of <typeparamref name="TActionResult"/>.
51+
/// </summary>
52+
/// <param name="reason">
53+
/// A formatted phrase as is supported by <see cref="string.Format(string,object[])" /> explaining why the assertion
54+
/// is needed. If the phrase does not start with the word <i>because</i>, it is prepended automatically.
55+
/// </param>
56+
/// <param name="reasonArgs">
57+
/// Zero or more objects to format using the placeholders in <paramref name="reason"/>.
58+
/// </param>
59+
/// <returns>
60+
/// An <see cref="AndWhichConstraint{TParentConstraint, TMatchedElement}"/> where the Which contains
61+
/// the result of Result converted to <typeparamref name="TActionResult"/>.
62+
/// </returns>
63+
[CustomAssertion]
64+
public AndWhichConstraint<ActionResultAssertions<TValue>, TActionResult> ConvertibleTo<TActionResult>(
65+
string reason = "", params object[] reasonArgs)
66+
where TActionResult : ActionResult
67+
{
68+
Execute.Assertion
69+
.BecauseOf(reason, reasonArgs)
70+
.ForCondition(Result.GetType() == typeof(TActionResult))
71+
.FailWith(FailureMessages.ConvertibleActionFailMessage, typeof(TActionResult), Result.GetType());
72+
73+
return new AndWhichConstraint<ActionResultAssertions<TValue>, TActionResult>(this, (TActionResult)Result);
74+
}
5375
}
76+
#endregion Public Methods
5477
}
55-
#endif

src/FluentAssertions.AspNetCore.Mvc/AssertionsExtensions.cs

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,6 @@ public static RouteDataAssertions Should(this RouteData routeData)
3838
return new RouteDataAssertions(routeData);
3939
}
4040

41-
#if NETCOREAPP3_0
42-
4341
/// <summary>
4442
/// Returns an <see cref="ConvertToActionResultAssertions"/> object that can be used to assert the
4543
/// current <see cref="IConvertToActionResult"/>.
@@ -48,7 +46,5 @@ public static ConvertToActionResultAssertions Should(this IConvertToActionResult
4846
{
4947
return new ConvertToActionResultAssertions(actual);
5048
}
51-
52-
#endif
5349
}
5450
}

src/FluentAssertions.AspNetCore.Mvc/ConvertToActionResultAssertions.cs

Lines changed: 37 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,7 @@
1-
#if NETCOREAPP3_0
2-
using FluentAssertions.Execution;
1+
using FluentAssertions.Execution;
32
using FluentAssertions.Primitives;
43
using Microsoft.AspNetCore.Mvc;
54
using Microsoft.AspNetCore.Mvc.Infrastructure;
6-
using System;
7-
using System.Collections.Generic;
8-
using System.Text;
95

106
namespace FluentAssertions.AspNetCore.Mvc
117
{
@@ -55,11 +51,43 @@ public ActionResultAssertions<TValue> BeActionResult<TValue>(string reason = "",
5551
Execute.Assertion
5652
.BecauseOf(reason, reasonArgs)
5753
.ForCondition(genericParameter == typeof(TValue))
58-
.FailWith("Expected {context} to be ActionResult<TValue> with type {0}{reason} but was {1}.", typeof(TValue), genericParameter);
54+
.FailWith(FailureMessages.ConvertibelModelFailMessage, typeof(TValue), genericParameter);
5955

6056
return new ActionResultAssertions<TValue>((ActionResult<TValue>)Subject);
6157
}
62-
}
63-
}
6458

65-
#endif
59+
/// <summary>
60+
/// Asserts that calling the subject's <see cref="IConvertToActionResult.Convert"/> method,
61+
/// the resulting <see cref="IActionResult"/> 's type is <typeparamref name="TActionResult"/>.
62+
/// </summary>
63+
/// <param name="reason">
64+
/// A formatted phrase as is supported by <see cref="string.Format(string,object[])" /> explaining why the assertion
65+
/// is needed. If the phrase does not start with the word <i>because</i>, it is prepended automatically.
66+
/// </param>
67+
/// <param name="reasonArgs">
68+
/// Zero or more objects to format using the placeholders in <paramref name="reason"/>.
69+
/// </param>
70+
/// <returns>
71+
/// An <see cref="AndWhichConstraint{TParentConstraint, TMatchedElement}"/> where the Which contains
72+
/// the result of Convert() converted to <typeparamref name="TActionResult"/>.
73+
/// </returns>
74+
[CustomAssertion]
75+
public AndWhichConstraint<ConvertToActionResultAssertions, TActionResult> ConvertibleTo<TActionResult>(
76+
string reason = "", params object[] reasonArgs)
77+
where TActionResult : class, IActionResult
78+
{
79+
var convertResult = Subject.Convert();
80+
Execute.Assertion
81+
.BecauseOf(reason, reasonArgs)
82+
.ForCondition(convertResult != null)
83+
.FailWith(FailureMessages.ConvertibleActionFailMessage, typeof(TActionResult), null);
84+
85+
Execute.Assertion
86+
.BecauseOf(reason, reasonArgs)
87+
.ForCondition(convertResult.GetType() == typeof(TActionResult))
88+
.FailWith(FailureMessages.ConvertibleActionFailMessage, typeof(TActionResult), convertResult.GetType());
89+
90+
return new AndWhichConstraint<ConvertToActionResultAssertions, TActionResult>(this, (TActionResult)convertResult);
91+
}
92+
}
93+
}

src/FluentAssertions.AspNetCore.Mvc/FailureMessages.Designer.cs

Lines changed: 18 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/FluentAssertions.AspNetCore.Mvc/FailureMessages.resx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,12 @@
135135
<data name="CommonTypeFailMessage" xml:space="preserve">
136136
<value>Expected {context} to be of type {0} but was {1}.</value>
137137
</data>
138+
<data name="ConvertibelModelFailMessage" xml:space="preserve">
139+
<value>Expected {context} to be ActionResult&lt;TValue&gt; with type {0}{reason} but was {1}.</value>
140+
</data>
141+
<data name="ConvertibleActionFailMessage" xml:space="preserve">
142+
<value>Expected {context} to be convertible to {0} but it was {1}.</value>
143+
</data>
138144
<data name="FileContentResult_WithFileContents_LengthFail" xml:space="preserve">
139145
<value>Expected FileContentResult.FileContents to have {0} byte(s){reason} but found {1}.</value>
140146
</data>

tests/FluentAssertions.AspNetCore.Mvc.Tests/ActionResultAssertionsOfTValue_Tests.cs

Lines changed: 43 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,5 @@
1-
#if NETCOREAPP3_0
2-
using FluentAssertions.AspNetCore.Mvc.Tests.Helpers;
31
using FluentAssertions.Mvc.Tests.Helpers;
42
using Microsoft.AspNetCore.Mvc;
5-
using Microsoft.AspNetCore.Routing;
63
using System;
74
using Xunit;
85

@@ -24,7 +21,48 @@ public void Result_GivenActionResultOfTValue_ShouldReturnSameValue()
2421
result.Should().BeActionResult<object>().Result.Should().BeSameAs(actionResult);
2522
}
2623

24+
[Fact]
25+
public void Value_GivenAValue_ShouldReturnSameValue()
26+
{
27+
var theValue = new object();
28+
var result = new ActionResult<object>(theValue);
29+
30+
result.Should().BeActionResult<object>().Value.Should().BeSameAs(theValue);
31+
}
32+
33+
[Fact]
34+
public void ConvertibleTo_CallingConvertResultsDifferentType_ShouldFail()
35+
{
36+
var result = new ActionResult<object>(new BadRequestObjectResult(new object()));
37+
var failureMessage = FailureMessageHelper.ExpectedContextToBeConvertible(
38+
"result", typeof(ActionResult).FullName, typeof(BadRequestObjectResult).FullName);
39+
40+
Action action = () => result.Should().BeActionResult<object>().ConvertibleTo<ActionResult>(Reason, ReasonArgs);
41+
42+
action.Should().Throw<Exception>()
43+
.WithMessage(failureMessage);
44+
45+
}
46+
47+
[Fact]
48+
public void ConvertibleTo_CallingConvertResultsGoodType_ShouldPass()
49+
{
50+
var result = new ActionResult<object>(new OkObjectResult(new object()));
51+
52+
result.Should().BeActionResult<object>().ConvertibleTo<OkObjectResult>(Reason, ReasonArgs);
53+
}
54+
55+
[Fact]
56+
public void ConvertibleToWich_ShouldBeTheConvertedObject()
57+
{
58+
OkObjectResult expectation = new OkObjectResult(new object());
59+
var result = new ActionResult<object>(expectation);
60+
61+
var actual =
62+
result.Should().BeActionResult<object>().ConvertibleTo<OkObjectResult>(Reason, ReasonArgs).Which;
63+
64+
actual.Should().BeSameAs(expectation);
65+
}
2766
#endregion Public Methods
2867
}
29-
}
30-
#endif
68+
}

tests/FluentAssertions.AspNetCore.Mvc.Tests/ConvertToActionResultAssertions_Tests.cs

Lines changed: 58 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,8 @@
1-
#if NETCOREAPP3_0
2-
using FluentAssertions.Mvc.Tests.Helpers;
1+
using FluentAssertions.Mvc.Tests.Helpers;
32
using Microsoft.AspNetCore.Mvc;
43
using Microsoft.AspNetCore.Mvc.Infrastructure;
54
using Moq;
65
using System;
7-
using System.Collections.Generic;
8-
using System.Text;
96
using Xunit;
107

118
namespace FluentAssertions.AspNetCore.Mvc.Tests
@@ -63,7 +60,8 @@ public void BeActionResult_GivenUnexpectedTValue_ShouldFail()
6360
[Fact]
6461
public void BeActionResult_GivenUnexpectedType_ShouldFail()
6562
{
66-
IConvertToActionResult result = new Mock<IConvertToActionResult>().Object;
63+
var mock = new Mock<IConvertToActionResult>();
64+
var result = mock.Object;
6765
var failureMessage = FailureMessageHelper.ExpectedContextTypeXButFoundY(
6866
"result", "Microsoft.AspNetCore.Mvc.ActionResult`1[System.Object]", result.GetType().FullName);
6967

@@ -73,8 +71,60 @@ public void BeActionResult_GivenUnexpectedType_ShouldFail()
7371
.WithMessage(failureMessage);
7472
}
7573

74+
[Fact]
75+
public void ConvertibleTo_CallingConvertResultsNull_ShouldFail()
76+
{
77+
var mock = new Mock<IConvertToActionResult>();
78+
mock.Setup(e => e.Convert()).Returns((IActionResult)null);
79+
var result = mock.Object;
80+
var failureMessage = FailureMessageHelper.ExpectedContextToBeConvertible(
81+
"result", typeof(ActionResult).FullName, "<null>");
7682

77-
}
78-
}
83+
Action action = () => result.Should().ConvertibleTo<ActionResult>(Reason, ReasonArgs);
84+
85+
action.Should().Throw<Exception>()
86+
.WithMessage(failureMessage);
87+
88+
}
89+
90+
[Fact]
91+
public void ConvertibleTo_CallingConvertResultsDifferentType_ShouldFail()
92+
{
93+
var mock = new Mock<IConvertToActionResult>();
94+
mock.Setup(e => e.Convert()).Returns(new BadRequestObjectResult(new object()));
95+
var result = mock.Object;
96+
var failureMessage = FailureMessageHelper.ExpectedContextToBeConvertible(
97+
"result", typeof(ActionResult).FullName, typeof(BadRequestObjectResult).FullName);
98+
99+
Action action = () => result.Should().ConvertibleTo<ActionResult>(Reason, ReasonArgs);
100+
101+
action.Should().Throw<Exception>()
102+
.WithMessage(failureMessage);
103+
104+
}
105+
106+
[Fact]
107+
public void ConvertibleTo_CallingConvertResultsGoodType_ShouldPass()
108+
{
109+
var mock = new Mock<IConvertToActionResult>();
110+
mock.Setup(e => e.Convert()).Returns(new OkObjectResult(new object()));
111+
var result = mock.Object;
112+
113+
result.Should().ConvertibleTo<OkObjectResult>(Reason, ReasonArgs);
114+
}
115+
116+
[Fact]
117+
public void ConvertibleToWich_ShouldBeTheConvertedObject()
118+
{
119+
var mock = new Mock<IConvertToActionResult>();
120+
var expectation = new OkObjectResult(new object());
121+
mock.Setup(e => e.Convert()).Returns(expectation);
122+
var result = mock.Object;
79123

80-
#endif
124+
var actual =
125+
result.Should().ConvertibleTo<OkObjectResult>(Reason, ReasonArgs).Which;
126+
127+
actual.Should().BeSameAs(expectation);
128+
}
129+
}
130+
}

tests/FluentAssertions.AspNetCore.Mvc.Tests/Helpers/FailureMessageHelper.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,11 @@ internal static string ExpectedContextTypeXButFoundNull(string context, Type exp
8585
return $"Expected {context} to be of type {expectedType}, but no value was supplied.";
8686
}
8787

88+
internal static string ExpectedContextToBeConvertible(string context, string expectedType, string actualType)
89+
{
90+
return $"Expected {context} to be convertible to {expectedType} but it was {actualType}.";
91+
}
92+
8893
internal static string ExpectedContextContainValueAtKeyButFoundNull(string context, string value, string key)
8994
{
9095
return $"Expected {context} to contain value \"{value}\" at key \"{key}\" because it is 10, but it is <null>.";

0 commit comments

Comments
 (0)