Skip to content

Commit fbf545e

Browse files
committed
Create basic ConvertToActionResultAssertions.
1 parent 02f1f92 commit fbf545e

File tree

6 files changed

+247
-1
lines changed

6 files changed

+247
-1
lines changed
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
#if NETCOREAPP3_0
2+
using FluentAssertions.Execution;
3+
using FluentAssertions.Primitives;
4+
using Microsoft.AspNetCore.Mvc;
5+
using System.Diagnostics;
6+
7+
namespace FluentAssertions.AspNetCore.Mvc
8+
{
9+
/// <summary>
10+
/// Contains a number of methods to assert that an <see cref="ActionResult"/> is in the expected state.
11+
/// </summary>
12+
[DebuggerNonUserCode]
13+
public class ActionResultAssertions<TValue> : ReferenceTypeAssertions<ActionResult<TValue>, ActionResultAssertions<TValue>>
14+
{
15+
#region Public Constructors
16+
17+
/// <summary>
18+
/// Initializes a new instance of the <see cref="ActionResultAssertions{TValue}" /> class.
19+
/// </summary>
20+
public ActionResultAssertions(ActionResult<TValue> subject)
21+
{
22+
Subject = subject;
23+
}
24+
25+
#endregion Public Constructors
26+
27+
#region Protected Properties
28+
29+
/// <inheritdoc />
30+
protected override string Identifier { get; } = $"ActionResult<{typeof(TValue).Name}>";
31+
32+
#endregion Protected Properties
33+
34+
#region Public Properties
35+
36+
/// <summary>
37+
/// Gets the <see cref="ActionResult{TValue}.Result"/> of the actual Subjhect.
38+
/// </summary>
39+
public ActionResult Result => Subject.Result;
40+
41+
/// <summary>
42+
/// Gets the <see cref="ActionResult{TValue}.Value"/> of the actual Subjhect.
43+
/// </summary>
44+
public TValue Value => Subject.Value;
45+
46+
#endregion Public Properties
47+
48+
#region Public Methods
49+
50+
/// TODO What should be here?
51+
52+
#endregion Public Methods
53+
}
54+
}
55+
#endif

src/FluentAssertions.AspNetCore.Mvc/AssertionsExtensions.cs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using Microsoft.AspNetCore.Mvc;
2+
using Microsoft.AspNetCore.Mvc.Infrastructure;
23
using Microsoft.AspNetCore.Routing;
34
using System.Diagnostics;
45

@@ -36,5 +37,18 @@ public static RouteDataAssertions Should(this RouteData routeData)
3637
{
3738
return new RouteDataAssertions(routeData);
3839
}
40+
41+
#if NETCOREAPP3_0
42+
43+
/// <summary>
44+
/// Returns an <see cref="ConvertToActionResultAssertions"/> object that can be used to assert the
45+
/// current <see cref="IConvertToActionResult"/>.
46+
/// </summary>
47+
public static ConvertToActionResultAssertions Should(this IConvertToActionResult actual)
48+
{
49+
return new ConvertToActionResultAssertions(actual);
50+
}
51+
52+
#endif
3953
}
4054
}
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
#if NETCOREAPP3_0
2+
using FluentAssertions.Execution;
3+
using FluentAssertions.Primitives;
4+
using Microsoft.AspNetCore.Mvc;
5+
using Microsoft.AspNetCore.Mvc.Infrastructure;
6+
using System;
7+
using System.Collections.Generic;
8+
using System.Text;
9+
10+
namespace FluentAssertions.AspNetCore.Mvc
11+
{
12+
/// <summary>
13+
/// Contains a number of methods to assert that an <see cref="IConvertToActionResult"/> is in the expected state.
14+
/// </summary>
15+
public class ConvertToActionResultAssertions : ReferenceTypeAssertions<IConvertToActionResult, ConvertToActionResultAssertions>
16+
{
17+
public ConvertToActionResultAssertions(IConvertToActionResult subject)
18+
{
19+
Subject = subject;
20+
}
21+
22+
/// <inheritdoc />
23+
protected override string Identifier => "IConvertToActionResult";
24+
25+
/// <summary>
26+
/// Asserts that the subject is a <see cref="ActionResult{TValue}"/>.
27+
/// </summary>
28+
/// <param name="reason">
29+
/// A formatted phrase as is supported by <see cref="string.Format(string,object[])" /> explaining why the assertion
30+
/// is needed. If the phrase does not start with the word <i>because</i>, it is prepended automatically.
31+
/// </param>
32+
/// <param name="reasonArgs">
33+
/// Zero or more objects to format using the placeholders in <paramref name="reason"/>.
34+
/// </param>
35+
[CustomAssertion]
36+
public ActionResultAssertions<TValue> BeActionResult<TValue>(string reason = "", params object[] reasonArgs)
37+
{
38+
Execute.Assertion
39+
.BecauseOf(reason, reasonArgs)
40+
.ForCondition(!(Subject is null))
41+
.FailWith(FailureMessages.CommonNullWasSuppliedFailMessage, typeof(ActionResult<TValue>));
42+
43+
var type = Subject.GetType();
44+
45+
Execute.Assertion
46+
.BecauseOf(reason, reasonArgs)
47+
.ForCondition(type.IsGenericType && type.GetGenericTypeDefinition() == typeof(ActionResult<>))
48+
.FailWith(FailureMessages.CommonTypeFailMessage, typeof(ActionResult<TValue>), Subject.GetType());
49+
50+
var genericParameter = type.GenericTypeArguments[0];
51+
52+
Execute.Assertion
53+
.BecauseOf(reason, reasonArgs)
54+
.ForCondition(genericParameter == typeof(TValue))
55+
.FailWith("Expected {context} to be ActionResult<TValue> with type {0}{reason} but was {1}.", typeof(TValue), genericParameter);
56+
57+
return new ActionResultAssertions<TValue>((ActionResult<TValue>)Subject);
58+
}
59+
}
60+
}
61+
62+
#endif
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
#if NETCOREAPP3_0
2+
using FluentAssertions.AspNetCore.Mvc.Tests.Helpers;
3+
using FluentAssertions.Mvc.Tests.Helpers;
4+
using Microsoft.AspNetCore.Mvc;
5+
using Microsoft.AspNetCore.Routing;
6+
using System;
7+
using Xunit;
8+
9+
namespace FluentAssertions.AspNetCore.Mvc.Tests
10+
{
11+
public class ActionResultAssertionsOfTValue_Tests
12+
{
13+
public const string Reason = FailureMessageHelper.Reason;
14+
public readonly static object[] ReasonArgs = FailureMessageHelper.ReasonArgs;
15+
16+
#region Public Methods
17+
18+
[Fact]
19+
public void Result_GivenActionResultOfTValue_ShouldReturnSameValue()
20+
{
21+
var actionResult = new BadRequestResult();
22+
var result = new ActionResult<object>(actionResult);
23+
24+
result.Should().BeActionResult<object>().Result.Should().BeSameAs(actionResult);
25+
}
26+
27+
#endregion Public Methods
28+
}
29+
}
30+
#endif
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
#if NETCOREAPP3_0
2+
using FluentAssertions.Mvc.Tests.Helpers;
3+
using Microsoft.AspNetCore.Mvc;
4+
using Microsoft.AspNetCore.Mvc.Infrastructure;
5+
using Moq;
6+
using System;
7+
using System.Collections.Generic;
8+
using System.Text;
9+
using Xunit;
10+
11+
namespace FluentAssertions.AspNetCore.Mvc.Tests
12+
{
13+
public class ConvertToActionResultAssertions_Tests
14+
{
15+
public const string Reason = FailureMessageHelper.Reason;
16+
public readonly static object[] ReasonArgs = FailureMessageHelper.ReasonArgs;
17+
18+
[Fact]
19+
public void BeActionResult_GivenActionResultOfTValue_ShouldPass()
20+
{
21+
var actionResult = new BadRequestResult();
22+
IConvertToActionResult result = new ActionResult<object>(actionResult);
23+
24+
result.Should().BeActionResult<object>();
25+
}
26+
27+
[Fact]
28+
public void BeActionResult_GivenActionResultOfTValue_ShouldReturnActionResultAssertionsOfTValue()
29+
{
30+
var actionResult = new BadRequestResult();
31+
IConvertToActionResult subject = new ActionResult<object>(actionResult);
32+
33+
var result = subject.Should().BeActionResult<object>();
34+
35+
result.Should().BeOfType<ActionResultAssertions<object>>();
36+
}
37+
38+
[Fact]
39+
public void BeActionResult_GivenNull_ShouldFail()
40+
{
41+
IConvertToActionResult result = null;
42+
var failureMessage = FailureMessageHelper.ExpectedContextTypeXButFoundNull(
43+
"result", typeof(ActionResult<object>));
44+
45+
Action action = () => result.Should().BeActionResult<object>(Reason, ReasonArgs);
46+
47+
action.Should().Throw<Exception>()
48+
.WithMessage(failureMessage);
49+
}
50+
51+
[Fact]
52+
public void BeActionResult_GivenUnexpectedTValue_ShouldFail()
53+
{
54+
var actionResult = new BadRequestResult();
55+
IConvertToActionResult result = new ActionResult<string>(actionResult);
56+
57+
Action action = () => result.Should().BeActionResult<int>(Reason, ReasonArgs);
58+
59+
action.Should().Throw<Exception>()
60+
.WithMessage("Expected result to be ActionResult<TValue> with type System.Int32 because it is 10 but was System.String.");
61+
}
62+
63+
[Fact]
64+
public void BeActionResult_GivenUnexpectedType_ShouldFail()
65+
{
66+
IConvertToActionResult result = new Mock<IConvertToActionResult>().Object;
67+
var failureMessage = FailureMessageHelper.ExpectedContextTypeXButFoundY(
68+
"result", "Microsoft.AspNetCore.Mvc.ActionResult`1[System.Object]", result.GetType().FullName);
69+
70+
Action action = () => result.Should().BeActionResult<object>(Reason, ReasonArgs);
71+
72+
action.Should().Throw<Exception>()
73+
.WithMessage(failureMessage);
74+
}
75+
76+
77+
}
78+
}
79+
80+
#endif

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

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,12 @@ private static object ToString(DateTimeOffset? expected)
7272

7373
internal static string ExpectedContextTypeXButFoundY(string context, Type expected, Type actual)
7474
{
75-
return $"Expected {context} to be of type {expected.FullName} but was {actual.FullName}.";
75+
return ExpectedContextTypeXButFoundY(context, expected.FullName, actual.FullName);
76+
}
77+
78+
internal static string ExpectedContextTypeXButFoundY(string context, string expected, string actual)
79+
{
80+
return $"Expected {context} to be of type {expected} but was {actual}.";
7681
}
7782

7883
internal static string ExpectedContextTypeXButFoundNull(string context, Type expectedType)

0 commit comments

Comments
 (0)