diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..f0a5027 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,31 @@ +############################################################################### +# Set default behavior to automatically normalize line endings. +############################################################################### +* text=auto eol=crlf + +############################################################################### +# Set default behavior for command prompt diff. +# +# This is need for earlier builds of msysgit that does not have it on by +# default for csharp files. +# Note: This is only used by command line +############################################################################### +*.cs text diff=csharp + +############################################################################### +# diff behavior for common document formats +# +# Convert binary document formats to text before diffing them. This feature +# is only available from the command line. Turn it on by uncommenting the +# entries below. +############################################################################### +*.doc diff=astextplain +*.DOC diff=astextplain +*.docx diff=astextplain +*.DOCX diff=astextplain +*.dot diff=astextplain +*.DOT diff=astextplain +*.pdf diff=astextplain +*.PDF diff=astextplain +*.rtf diff=astextplain +*.RTF diff=astextplain diff --git a/FluentAssertions.AspNetCore.Mvc.sln b/FluentAssertions.AspNetCore.Mvc.sln index 311968c..f9c4065 100644 --- a/FluentAssertions.AspNetCore.Mvc.sln +++ b/FluentAssertions.AspNetCore.Mvc.sln @@ -16,6 +16,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{A4C33565 EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FluentAssertions.AspNetCore.Mvc", "src\FluentAssertions.AspNetCore.Mvc\FluentAssertions.AspNetCore.Mvc.csproj", "{EA71D220-089E-4CB1-B187-D9BB4DFD8624}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FluentAssertions.AspNetCore.Mvc.Ref", "src\FluentAssertions.AspNetCore.Mvc.Ref\FluentAssertions.AspNetCore.Mvc.Ref.csproj", "{BA15EEA1-7305-42D3-A60E-3BDEC8CBECAF}" +EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FluentAssertions.AspNetCore.Mvc.Tests", "tests\FluentAssertions.AspNetCore.Mvc.Tests\FluentAssertions.AspNetCore.Mvc.Tests.csproj", "{A4962D4A-A99A-4F96-8F76-C5A2F1D06577}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FluentAssertions.AspNetCore.Mvc.Sample.Tests", "samples\FluentAssertions.AspNetCore.Mvc.Sample.Tests\FluentAssertions.AspNetCore.Mvc.Sample.Tests.csproj", "{BCFB2639-D0CC-4E81-B431-8CB37F66480F}" @@ -44,6 +46,18 @@ Global {EA71D220-089E-4CB1-B187-D9BB4DFD8624}.Release|Mixed Platforms.Build.0 = Release|Any CPU {EA71D220-089E-4CB1-B187-D9BB4DFD8624}.Release|x86.ActiveCfg = Release|Any CPU {EA71D220-089E-4CB1-B187-D9BB4DFD8624}.Release|x86.Build.0 = Release|Any CPU + {BA15EEA1-7305-42D3-A60E-3BDEC8CBECAF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {BA15EEA1-7305-42D3-A60E-3BDEC8CBECAF}.Debug|Any CPU.Build.0 = Debug|Any CPU + {BA15EEA1-7305-42D3-A60E-3BDEC8CBECAF}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {BA15EEA1-7305-42D3-A60E-3BDEC8CBECAF}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {BA15EEA1-7305-42D3-A60E-3BDEC8CBECAF}.Debug|x86.ActiveCfg = Debug|Any CPU + {BA15EEA1-7305-42D3-A60E-3BDEC8CBECAF}.Debug|x86.Build.0 = Debug|Any CPU + {BA15EEA1-7305-42D3-A60E-3BDEC8CBECAF}.Release|Any CPU.ActiveCfg = Release|Any CPU + {BA15EEA1-7305-42D3-A60E-3BDEC8CBECAF}.Release|Any CPU.Build.0 = Release|Any CPU + {BA15EEA1-7305-42D3-A60E-3BDEC8CBECAF}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {BA15EEA1-7305-42D3-A60E-3BDEC8CBECAF}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {BA15EEA1-7305-42D3-A60E-3BDEC8CBECAF}.Release|x86.ActiveCfg = Release|Any CPU + {BA15EEA1-7305-42D3-A60E-3BDEC8CBECAF}.Release|x86.Build.0 = Release|Any CPU {A4962D4A-A99A-4F96-8F76-C5A2F1D06577}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {A4962D4A-A99A-4F96-8F76-C5A2F1D06577}.Debug|Any CPU.Build.0 = Debug|Any CPU {A4962D4A-A99A-4F96-8F76-C5A2F1D06577}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU @@ -86,6 +100,7 @@ Global EndGlobalSection GlobalSection(NestedProjects) = preSolution {EA71D220-089E-4CB1-B187-D9BB4DFD8624} = {89B09C65-4F1E-4B5A-AD58-E244113F2C02} + {BA15EEA1-7305-42D3-A60E-3BDEC8CBECAF} = {89B09C65-4F1E-4B5A-AD58-E244113F2C02} {A4962D4A-A99A-4F96-8F76-C5A2F1D06577} = {A4C33565-1E6E-4AE7-8F02-1911DBA00263} {BCFB2639-D0CC-4E81-B431-8CB37F66480F} = {4E91D524-3DC0-4E08-82FE-6FDAEB89212B} {263F58EE-CF19-4974-AEBB-2A0E6C3F9054} = {4E91D524-3DC0-4E08-82FE-6FDAEB89212B} diff --git a/appveyor.yml b/appveyor.yml index 26b4d45..cfd43af 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,5 +1,5 @@ version: '{build}' -image: Visual Studio 2017 +image: Visual Studio 2019 configuration: Release before_build: diff --git a/global.json b/global.json new file mode 100644 index 0000000..80d84a2 --- /dev/null +++ b/global.json @@ -0,0 +1,5 @@ +{ + "msbuild-sdks": { + "MSBuild.Sdk.Extras": "2.0.54" + } +} \ No newline at end of file diff --git a/samples/FluentAssertions.AspNetCore.Mvc.Sample.Tests/ApiController_Tests.cs b/samples/FluentAssertions.AspNetCore.Mvc.Sample.Tests/ApiController_Tests.cs index 11a7db4..2640ad8 100644 --- a/samples/FluentAssertions.AspNetCore.Mvc.Sample.Tests/ApiController_Tests.cs +++ b/samples/FluentAssertions.AspNetCore.Mvc.Sample.Tests/ApiController_Tests.cs @@ -1,14 +1,11 @@ -using System; -using System.Collections.Generic; -using System.Net.Mime; -using System.Security.Claims; -using FluentAssertions.AspNetCore.Mvc.Sample.Controllers; +using FluentAssertions.AspNetCore.Mvc.Sample.Controllers; using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Mvc.ModelBinding; -using Microsoft.AspNetCore.Routing; -using Microsoft.EntityFrameworkCore.ValueGeneration.Internal; using Microsoft.Extensions.Primitives; using Microsoft.Net.Http.Headers; +using System; +using System.Collections.Generic; +using System.Security.Claims; using Xunit; namespace FluentAssertions.AspNetCore.Mvc.Sample.Tests diff --git a/samples/FluentAssertions.AspNetCore.Mvc.Sample.Tests/FluentAssertions.AspNetCore.Mvc.Sample.Tests.csproj b/samples/FluentAssertions.AspNetCore.Mvc.Sample.Tests/FluentAssertions.AspNetCore.Mvc.Sample.Tests.csproj index 5fbad66..d3342e1 100644 --- a/samples/FluentAssertions.AspNetCore.Mvc.Sample.Tests/FluentAssertions.AspNetCore.Mvc.Sample.Tests.csproj +++ b/samples/FluentAssertions.AspNetCore.Mvc.Sample.Tests/FluentAssertions.AspNetCore.Mvc.Sample.Tests.csproj @@ -1,7 +1,7 @@ - netcoreapp2.0 + netcoreapp2.1;netcoreapp3.0 false diff --git a/samples/FluentAssertions.AspNetCore.Mvc.Sample.Tests/ProductController_Tests.cs b/samples/FluentAssertions.AspNetCore.Mvc.Sample.Tests/ProductController_Tests.cs index 847759f..f0c984f 100644 --- a/samples/FluentAssertions.AspNetCore.Mvc.Sample.Tests/ProductController_Tests.cs +++ b/samples/FluentAssertions.AspNetCore.Mvc.Sample.Tests/ProductController_Tests.cs @@ -1,4 +1,6 @@ using FluentAssertions.AspNetCore.Mvc.Sample.Controllers; +using Microsoft.AspNetCore.Mvc; +using System; using Xunit; namespace FluentAssertions.AspNetCore.Mvc.Sample.Tests @@ -13,5 +15,34 @@ public void List_ShouldReturnView() .BeViewResult() .WithViewName("Index"); } + + [Fact] + public void GetActionResultOfT_OnFalse_Returns_Data() + { + var controller = new ProductController(); + var model = new Models.ProductViewModel { Id = 1, Name = "Text" }; + var returnError = false; + + var result = controller.GetActionResultOfT(model, returnError); + + result.Should().BeConvertibleTo() + .And.Value.Should().BeSameAs(model); + } + + [Fact] + public void GetActionResultOfT_OnTrue_Returns_BadRequest() + { + var controller = new ProductController(); + var model = new Models.ProductViewModel { Id = 1, Name = "Text" }; + var returnError = true; + + var result = controller.GetActionResultOfT(model, returnError); + + result.Should().BeConvertibleTo() + .Which.Value.Should().BeSameAs(model); + + result.Should().BeConvertibleTo() + .Which.Should().BeBadRequestObjectResult(); + } } } diff --git a/samples/FluentAssertions.AspNetCore.Mvc.Sample/Controllers/ProductController.cs b/samples/FluentAssertions.AspNetCore.Mvc.Sample/Controllers/ProductController.cs index f095c7c..6991ad6 100644 --- a/samples/FluentAssertions.AspNetCore.Mvc.Sample/Controllers/ProductController.cs +++ b/samples/FluentAssertions.AspNetCore.Mvc.Sample/Controllers/ProductController.cs @@ -1,4 +1,5 @@ -using Microsoft.AspNetCore.Mvc; +using FluentAssertions.AspNetCore.Mvc.Sample.Models; +using Microsoft.AspNetCore.Mvc; namespace FluentAssertions.AspNetCore.Mvc.Sample.Controllers { @@ -9,5 +10,18 @@ public IActionResult List() { return View("Index"); } + + #region ActionResult + [HttpGet] + public ActionResult GetActionResultOfT( + ProductViewModel data, bool error) + { + if (error) + { + return BadRequest(data); + } + return data; + } + #endregion } } diff --git a/samples/FluentAssertions.AspNetCore.Mvc.Sample/FluentAssertions.AspNetCore.Mvc.Sample.csproj b/samples/FluentAssertions.AspNetCore.Mvc.Sample/FluentAssertions.AspNetCore.Mvc.Sample.csproj index d7c2e49..ee26884 100644 --- a/samples/FluentAssertions.AspNetCore.Mvc.Sample/FluentAssertions.AspNetCore.Mvc.Sample.csproj +++ b/samples/FluentAssertions.AspNetCore.Mvc.Sample/FluentAssertions.AspNetCore.Mvc.Sample.csproj @@ -1,11 +1,15 @@ - netcoreapp2.0 + netcoreapp2.1;netcoreapp3.0 - - + + + + + + diff --git a/samples/FluentAssertions.AspNetCore.Mvc.Sample/Startup.cs b/samples/FluentAssertions.AspNetCore.Mvc.Sample/Startup.cs index a0b8280..4f7d319 100644 --- a/samples/FluentAssertions.AspNetCore.Mvc.Sample/Startup.cs +++ b/samples/FluentAssertions.AspNetCore.Mvc.Sample/Startup.cs @@ -1,8 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; @@ -28,24 +24,38 @@ public Startup(IHostingEnvironment env) public void ConfigureServices(IServiceCollection services) { // Add framework services. - services.AddMvc(); + services.AddMvc(options => + { +#if NETCOREAPP3_0 + options.EnableEndpointRouting = false; +#endif + }); + + services.AddLogging(configure => + { + configure.AddConsole(); + configure.AddDebug(); + }); } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) { - loggerFactory.AddConsole(Configuration.GetSection("Logging")); - loggerFactory.AddDebug(); - if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); +#if NETCOREAPP2_1 app.UseBrowserLink(); +#endif } else { app.UseExceptionHandler("/Home/Error"); } +#if NETCOREAPP2_1 + loggerFactory.AddConsole(Configuration.GetSection("Logging")); + loggerFactory.AddDebug(); +#endif app.UseStaticFiles(); diff --git a/src/FluentAssertions.AspNetCore.Mvc.Ref/FluentAssertions.AspNetCore.Mvc.Ref.csproj b/src/FluentAssertions.AspNetCore.Mvc.Ref/FluentAssertions.AspNetCore.Mvc.Ref.csproj new file mode 100644 index 0000000..c26922d --- /dev/null +++ b/src/FluentAssertions.AspNetCore.Mvc.Ref/FluentAssertions.AspNetCore.Mvc.Ref.csproj @@ -0,0 +1,22 @@ + + + + netstandard2.0;netcoreapp3.0 + + + + + + + + + + + + + + + + + + diff --git a/src/FluentAssertions.AspNetCore.Mvc/AcceptedAtActionResultAssertions.cs b/src/FluentAssertions.AspNetCore.Mvc/AcceptedAtActionResultAssertions.cs index 23c878f..b05f417 100644 --- a/src/FluentAssertions.AspNetCore.Mvc/AcceptedAtActionResultAssertions.cs +++ b/src/FluentAssertions.AspNetCore.Mvc/AcceptedAtActionResultAssertions.cs @@ -13,7 +13,7 @@ namespace FluentAssertions.AspNetCore.Mvc public class AcceptedAtActionResultAssertions : ObjectAssertions { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// public AcceptedAtActionResultAssertions(AcceptedAtActionResult subject) : base(subject) { } diff --git a/src/FluentAssertions.AspNetCore.Mvc/AcceptedAtRouteResultAssertions.cs b/src/FluentAssertions.AspNetCore.Mvc/AcceptedAtRouteResultAssertions.cs index 281d069..1b4d9ef 100644 --- a/src/FluentAssertions.AspNetCore.Mvc/AcceptedAtRouteResultAssertions.cs +++ b/src/FluentAssertions.AspNetCore.Mvc/AcceptedAtRouteResultAssertions.cs @@ -13,7 +13,7 @@ namespace FluentAssertions.AspNetCore.Mvc public class AcceptedAtRouteResultAssertions : ObjectAssertions { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// public AcceptedAtRouteResultAssertions(AcceptedAtRouteResult subject) : base(subject) { } diff --git a/src/FluentAssertions.AspNetCore.Mvc/AcceptedResultAssertions.cs b/src/FluentAssertions.AspNetCore.Mvc/AcceptedResultAssertions.cs index 6f02822..93c7881 100644 --- a/src/FluentAssertions.AspNetCore.Mvc/AcceptedResultAssertions.cs +++ b/src/FluentAssertions.AspNetCore.Mvc/AcceptedResultAssertions.cs @@ -15,7 +15,7 @@ public class AcceptedResultAssertions : ObjectAssertions #region Public Constructors /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The object to test assertion on public AcceptedResultAssertions(AcceptedResult subject) : base(subject) diff --git a/src/FluentAssertions.AspNetCore.Mvc/ActionResultAssertions.cs b/src/FluentAssertions.AspNetCore.Mvc/ActionResultAssertions.cs index 9b6ef58..f893caa 100644 --- a/src/FluentAssertions.AspNetCore.Mvc/ActionResultAssertions.cs +++ b/src/FluentAssertions.AspNetCore.Mvc/ActionResultAssertions.cs @@ -14,7 +14,7 @@ public class ActionResultAssertions : ObjectAssertions #region Public Constructors /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// public ActionResultAssertions(IActionResult subject) : base(subject) { diff --git a/src/FluentAssertions.AspNetCore.Mvc/ActionResultAssertionsOfTValue.cs b/src/FluentAssertions.AspNetCore.Mvc/ActionResultAssertionsOfTValue.cs new file mode 100644 index 0000000..5582e44 --- /dev/null +++ b/src/FluentAssertions.AspNetCore.Mvc/ActionResultAssertionsOfTValue.cs @@ -0,0 +1,84 @@ +using FluentAssertions.Execution; +using FluentAssertions.Primitives; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.Infrastructure; +using System.Diagnostics; + +namespace FluentAssertions.AspNetCore.Mvc +{ + /// + /// Contains a number of methods to assert that an is in the expected state. + /// + [DebuggerNonUserCode] + public class ActionResultAssertions : ReferenceTypeAssertions, ActionResultAssertions> + { + #region Public Constructors + + /// + /// Initializes a new instance of the class. + /// + public ActionResultAssertions(ActionResult subject) + { + Subject = subject; + } + + #endregion Public Constructors + + #region Protected Properties + + /// + protected override string Identifier { get; } = $"ActionResult<{typeof(TValue).Name}>"; + + #endregion Protected Properties + + #region Public Properties + + /// + /// Gets the of the Subject. + /// + public ActionResult Result => Subject.Result; + + /// + /// Gets the of the Subject. + /// + public TValue Value => Subject.Value; + + #endregion Public Properties + + #region Public Methods + + /// + /// 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 . + /// + /// + /// An where the Which contains + /// the result of Result converted to . + /// + [CustomAssertion] + public AndWhichConstraint, TActionResult> BeConvertibleTo( + string reason = "", params object[] reasonArgs) + where TActionResult : ActionResult + { + var convertResult = ((IConvertToActionResult)Subject).Convert(); + Execute.Assertion + .BecauseOf(reason, reasonArgs) + .ForCondition(convertResult != null) + .FailWith(FailureMessages.ConvertibleActionFailMessage, typeof(TActionResult), null); + + Execute.Assertion + .BecauseOf(reason, reasonArgs) + .ForCondition(convertResult.GetType() == typeof(TActionResult)) + .FailWith(FailureMessages.ConvertibleActionFailMessage, typeof(TActionResult), convertResult.GetType()); + + return new AndWhichConstraint, TActionResult>(this, (TActionResult)convertResult); + } + } + #endregion Public Methods +} diff --git a/src/FluentAssertions.AspNetCore.Mvc/AssertionsExtensions.cs b/src/FluentAssertions.AspNetCore.Mvc/AssertionsExtensions.cs index 7324789..37f3a9f 100644 --- a/src/FluentAssertions.AspNetCore.Mvc/AssertionsExtensions.cs +++ b/src/FluentAssertions.AspNetCore.Mvc/AssertionsExtensions.cs @@ -1,4 +1,5 @@ using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.Infrastructure; using Microsoft.AspNetCore.Routing; using System.Diagnostics; @@ -36,5 +37,24 @@ public static RouteDataAssertions Should(this RouteData routeData) { return new RouteDataAssertions(routeData); } + + /// + /// Returns an object that can be used to assert the + /// current . + /// + public static ConvertToActionResultAssertions Should(this IConvertToActionResult actual) + { + return new ConvertToActionResultAssertions(actual); + } + + + /// + /// Returns an object that can be used to assert the + /// current . + /// + public static ActionResultAssertions Should(this ActionResult actual) + { + return new ActionResultAssertions(actual); + } } } diff --git a/src/FluentAssertions.AspNetCore.Mvc/BadRequestObjectResultAssertions.cs b/src/FluentAssertions.AspNetCore.Mvc/BadRequestObjectResultAssertions.cs index d33de7a..fb550d7 100644 --- a/src/FluentAssertions.AspNetCore.Mvc/BadRequestObjectResultAssertions.cs +++ b/src/FluentAssertions.AspNetCore.Mvc/BadRequestObjectResultAssertions.cs @@ -13,7 +13,7 @@ public class BadRequestObjectResultAssertions : ObjectAssertions { #region Public Constructors /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The object to test assertion on public BadRequestObjectResultAssertions(BadRequestObjectResult subject) : base(subject) diff --git a/src/FluentAssertions.AspNetCore.Mvc/ChallengeResultAssertions.cs b/src/FluentAssertions.AspNetCore.Mvc/ChallengeResultAssertions.cs index 8639080..e8fc98b 100644 --- a/src/FluentAssertions.AspNetCore.Mvc/ChallengeResultAssertions.cs +++ b/src/FluentAssertions.AspNetCore.Mvc/ChallengeResultAssertions.cs @@ -19,7 +19,7 @@ public class ChallengeResultAssertions : ObjectAssertions #region Public Constructors /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The object to test assertion on public ChallengeResultAssertions(object subject) : base(subject) diff --git a/src/FluentAssertions.AspNetCore.Mvc/ContentResultAssertions.cs b/src/FluentAssertions.AspNetCore.Mvc/ContentResultAssertions.cs index cfe4efd..e355043 100644 --- a/src/FluentAssertions.AspNetCore.Mvc/ContentResultAssertions.cs +++ b/src/FluentAssertions.AspNetCore.Mvc/ContentResultAssertions.cs @@ -13,7 +13,7 @@ namespace FluentAssertions.AspNetCore.Mvc public class ContentResultAssertions : ObjectAssertions { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// public ContentResultAssertions(ContentResult subject) : base(subject) { diff --git a/src/FluentAssertions.AspNetCore.Mvc/ConvertToActionResultAssertions.cs b/src/FluentAssertions.AspNetCore.Mvc/ConvertToActionResultAssertions.cs new file mode 100644 index 0000000..a512738 --- /dev/null +++ b/src/FluentAssertions.AspNetCore.Mvc/ConvertToActionResultAssertions.cs @@ -0,0 +1,93 @@ +using FluentAssertions.Execution; +using FluentAssertions.Primitives; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.Infrastructure; + +namespace FluentAssertions.AspNetCore.Mvc +{ + /// + /// Contains a number of methods to assert that an is in the expected state. + /// + public class ConvertToActionResultAssertions : ReferenceTypeAssertions + { + /// + /// Initializes a new instance of the class. + /// + public ConvertToActionResultAssertions(IConvertToActionResult subject) + { + Subject = subject; + } + + /// + protected override string Identifier => "IConvertToActionResult"; + + /// + /// 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 . + /// + [CustomAssertion] + public ActionResultAssertions BeActionResult(string reason = "", params object[] reasonArgs) + { + Execute.Assertion + .BecauseOf(reason, reasonArgs) + .ForCondition(!(Subject is null)) + .FailWith(FailureMessages.CommonNullWasSuppliedFailMessage, typeof(ActionResult)); + + var type = Subject.GetType(); + + Execute.Assertion + .BecauseOf(reason, reasonArgs) + .ForCondition(type.IsGenericType && type.GetGenericTypeDefinition() == typeof(ActionResult<>)) + .FailWith(FailureMessages.CommonTypeFailMessage, typeof(ActionResult), Subject.GetType()); + + var genericParameter = type.GenericTypeArguments[0]; + + Execute.Assertion + .BecauseOf(reason, reasonArgs) + .ForCondition(genericParameter == typeof(TValue)) + .FailWith(FailureMessages.ConvertibelModelFailMessage, typeof(TValue), genericParameter); + + return new ActionResultAssertions((ActionResult)Subject); + } + + /// + /// Asserts that calling the subject's method, + /// the resulting 's type is . + /// + /// + /// 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 . + /// + /// + /// An where the Which contains + /// the result of Convert() converted to . + /// + [CustomAssertion] + public AndWhichConstraint BeConvertibleTo( + string reason = "", params object[] reasonArgs) + where TActionResult : class, IActionResult + { + var convertResult = Subject.Convert(); + Execute.Assertion + .BecauseOf(reason, reasonArgs) + .ForCondition(convertResult != null) + .FailWith(FailureMessages.ConvertibleActionFailMessage, typeof(TActionResult), null); + + Execute.Assertion + .BecauseOf(reason, reasonArgs) + .ForCondition(convertResult.GetType() == typeof(TActionResult)) + .FailWith(FailureMessages.ConvertibleActionFailMessage, typeof(TActionResult), convertResult.GetType()); + + return new AndWhichConstraint(this, (TActionResult)convertResult); + } + } +} \ No newline at end of file diff --git a/src/FluentAssertions.AspNetCore.Mvc/CreatedAtActionResultAssertions.cs b/src/FluentAssertions.AspNetCore.Mvc/CreatedAtActionResultAssertions.cs index 278bd3f..2ef6c83 100644 --- a/src/FluentAssertions.AspNetCore.Mvc/CreatedAtActionResultAssertions.cs +++ b/src/FluentAssertions.AspNetCore.Mvc/CreatedAtActionResultAssertions.cs @@ -13,7 +13,7 @@ namespace FluentAssertions.AspNetCore.Mvc public class CreatedAtActionResultAssertions : ObjectAssertions { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// public CreatedAtActionResultAssertions(CreatedAtActionResult subject) : base(subject) { } diff --git a/src/FluentAssertions.AspNetCore.Mvc/CreatedAtRouteResultAssertions.cs b/src/FluentAssertions.AspNetCore.Mvc/CreatedAtRouteResultAssertions.cs index b908ef5..c3e0fcf 100644 --- a/src/FluentAssertions.AspNetCore.Mvc/CreatedAtRouteResultAssertions.cs +++ b/src/FluentAssertions.AspNetCore.Mvc/CreatedAtRouteResultAssertions.cs @@ -13,7 +13,7 @@ namespace FluentAssertions.AspNetCore.Mvc public class CreatedAtRouteResultAssertions : ObjectAssertions { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// public CreatedAtRouteResultAssertions(CreatedAtRouteResult subject) : base(subject) { } diff --git a/src/FluentAssertions.AspNetCore.Mvc/CreatedResultAssertions.cs b/src/FluentAssertions.AspNetCore.Mvc/CreatedResultAssertions.cs index 875113c..22e21b5 100644 --- a/src/FluentAssertions.AspNetCore.Mvc/CreatedResultAssertions.cs +++ b/src/FluentAssertions.AspNetCore.Mvc/CreatedResultAssertions.cs @@ -15,7 +15,7 @@ public class CreatedResultAssertions : ObjectAssertions #region Public Constructors /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The object to test assertion on public CreatedResultAssertions(CreatedResult subject) : base(subject) diff --git a/src/FluentAssertions.AspNetCore.Mvc/FailureMessages.Designer.cs b/src/FluentAssertions.AspNetCore.Mvc/FailureMessages.Designer.cs index 45d59d4..7aa234b 100644 --- a/src/FluentAssertions.AspNetCore.Mvc/FailureMessages.Designer.cs +++ b/src/FluentAssertions.AspNetCore.Mvc/FailureMessages.Designer.cs @@ -114,6 +114,24 @@ internal static string CommonTypeFailMessage { } } + /// + /// Looks up a localized string similar to Expected {context} to be ActionResult<TValue> with type {0}{reason} but was {1}.. + /// + internal static string ConvertibelModelFailMessage { + get { + return ResourceManager.GetString("ConvertibelModelFailMessage", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Expected {context} to be convertible to {0} but it was {1}.. + /// + internal static string ConvertibleActionFailMessage { + get { + return ResourceManager.GetString("ConvertibleActionFailMessage", resourceCulture); + } + } + /// /// Looks up a localized string similar to Expected FileContentResult.FileContents to have {0} byte(s){reason} but found {1}.. /// diff --git a/src/FluentAssertions.AspNetCore.Mvc/FailureMessages.resx b/src/FluentAssertions.AspNetCore.Mvc/FailureMessages.resx index 2e8c80f..d62f4d3 100644 --- a/src/FluentAssertions.AspNetCore.Mvc/FailureMessages.resx +++ b/src/FluentAssertions.AspNetCore.Mvc/FailureMessages.resx @@ -135,6 +135,12 @@ Expected {context} to be of type {0} but was {1}. + + Expected {context} to be ActionResult<TValue> with type {0}{reason} but was {1}. + + + Expected {context} to be convertible to {0} but it was {1}. + Expected FileContentResult.FileContents to have {0} byte(s){reason} but found {1}. diff --git a/src/FluentAssertions.AspNetCore.Mvc/FluentAssertions.AspNetCore.Mvc.csproj b/src/FluentAssertions.AspNetCore.Mvc/FluentAssertions.AspNetCore.Mvc.csproj index 385cd5a..aec2bd8 100644 --- a/src/FluentAssertions.AspNetCore.Mvc/FluentAssertions.AspNetCore.Mvc.csproj +++ b/src/FluentAssertions.AspNetCore.Mvc/FluentAssertions.AspNetCore.Mvc.csproj @@ -1,13 +1,13 @@ - + Fluent Assertions extensions for ASP.NET Core MVC. Copyright 2018 Fluent Assertions extensions for ASP.NET Core MVC Fluent Assertions for ASP.NET Core MVC - 2.4.0 + 2.5.0 Casey Burns;Kevin Kuszyk - netstandard2.0 + netstandard2.0;netcoreapp3.0 FluentAssertions.AspNetCore.Mvc FluentAssertions.AspNetCore.Mvc TDD;TDD;Fluent;Mvc;AspNetMvc;aspnetcore;aspnetcoremvc @@ -18,20 +18,26 @@ false false true + Library + bin\$(Configuration)\$(TargetFramework)\FluentAssertions.AspNetCore.Mvc.xml + bin\$(Configuration)\$(TargetFramework)\ - - bin\Debug\netstandard2.0\ - bin\Debug\netstandard2.0\FluentAssertions.AspNetCore.Mvc.xml - + + + - - bin\Release\netstandard2.0\FluentAssertions.AspNetCore.Mvc.xml - + + + + + + + - - + + @@ -40,9 +46,6 @@ True FailureMessages.resx - - - ResXFileCodeGenerator FailureMessages.Designer.cs @@ -50,3 +53,4 @@ + diff --git a/src/FluentAssertions.AspNetCore.Mvc/ForbidResultAssertions.cs b/src/FluentAssertions.AspNetCore.Mvc/ForbidResultAssertions.cs index 735c1b2..99ba954 100644 --- a/src/FluentAssertions.AspNetCore.Mvc/ForbidResultAssertions.cs +++ b/src/FluentAssertions.AspNetCore.Mvc/ForbidResultAssertions.cs @@ -18,7 +18,7 @@ public class ForbidResultAssertions : ObjectAssertions #region Public Constructors /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The object to test assertion on public ForbidResultAssertions(ForbidResult subject) : base(subject) diff --git a/src/FluentAssertions.AspNetCore.Mvc/JsonResultAssertions.cs b/src/FluentAssertions.AspNetCore.Mvc/JsonResultAssertions.cs index eceada5..9af7d81 100644 --- a/src/FluentAssertions.AspNetCore.Mvc/JsonResultAssertions.cs +++ b/src/FluentAssertions.AspNetCore.Mvc/JsonResultAssertions.cs @@ -1,7 +1,6 @@ -using FluentAssertions.Execution; +using FluentAssertions.Execution; using FluentAssertions.Primitives; using Microsoft.AspNetCore.Mvc; -using Newtonsoft.Json; using System; using System.Diagnostics; @@ -16,7 +15,7 @@ public class JsonResultAssertions : ObjectAssertions #region Public Constructors /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The object to test assertion on public JsonResultAssertions(JsonResult subject) : base(subject) @@ -31,26 +30,30 @@ public JsonResultAssertions(JsonResult subject) : base(subject) /// /// The on the tested subject. /// - public JsonSerializerSettings SerializerSettings => JsonResultSubject.SerializerSettings; - +#if NETCOREAPP3_0 + public object SerializerSettings => JsonResultSubject.SerializerSettings; +#else + public Newtonsoft.Json.JsonSerializerSettings SerializerSettings => JsonResultSubject.SerializerSettings; +#endif + /// /// The on the tested subject. - /// - public object Value => JsonResultSubject.Value; - - #endregion - - #region Private Properties - - private JsonResult JsonResultSubject => (JsonResult)Subject; - - #endregion Private Properties - - #region Public Methods - - /// - /// Asserts that the content type is the expected content type. + /// + public object Value => JsonResultSubject.Value; + + #endregion + + #region Private Properties + + private JsonResult JsonResultSubject => (JsonResult)Subject; + + #endregion Private Properties + + #region Public Methods + + /// + /// Asserts that the content type is the expected content type. /// /// The expected content type. /// @@ -118,6 +121,6 @@ public TValue ValueAs() return (TValue)value; } - #endregion +#endregion } } \ No newline at end of file diff --git a/src/FluentAssertions.AspNetCore.Mvc/LocalRedirectResultAssertions.cs b/src/FluentAssertions.AspNetCore.Mvc/LocalRedirectResultAssertions.cs index f63b5b0..51101be 100644 --- a/src/FluentAssertions.AspNetCore.Mvc/LocalRedirectResultAssertions.cs +++ b/src/FluentAssertions.AspNetCore.Mvc/LocalRedirectResultAssertions.cs @@ -14,7 +14,7 @@ public class LocalRedirectResultAssertions : ObjectAssertions #region Public Constructors /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The object to test assertion on public LocalRedirectResultAssertions(LocalRedirectResult subject) : base(subject) diff --git a/src/FluentAssertions.AspNetCore.Mvc/NotFoundObjectResultAssertions.cs b/src/FluentAssertions.AspNetCore.Mvc/NotFoundObjectResultAssertions.cs index 1d9d7d9..c70d8b6 100644 --- a/src/FluentAssertions.AspNetCore.Mvc/NotFoundObjectResultAssertions.cs +++ b/src/FluentAssertions.AspNetCore.Mvc/NotFoundObjectResultAssertions.cs @@ -14,7 +14,7 @@ public class NotFoundObjectResultAssertions : ObjectAssertions #region Public Constructors /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The object to test assertion on public NotFoundObjectResultAssertions(NotFoundObjectResult subject) : base(subject) diff --git a/src/FluentAssertions.AspNetCore.Mvc/OkObjectResultAssertions.cs b/src/FluentAssertions.AspNetCore.Mvc/OkObjectResultAssertions.cs index dd7288c..e19de8c 100644 --- a/src/FluentAssertions.AspNetCore.Mvc/OkObjectResultAssertions.cs +++ b/src/FluentAssertions.AspNetCore.Mvc/OkObjectResultAssertions.cs @@ -14,7 +14,7 @@ public class OkObjectResultAssertions : ObjectAssertions #region Public Constructors /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The object to test assertion on public OkObjectResultAssertions(OkObjectResult subject) : base(subject) diff --git a/src/FluentAssertions.AspNetCore.Mvc/PartialViewResultAssertions.cs b/src/FluentAssertions.AspNetCore.Mvc/PartialViewResultAssertions.cs index 62d1b12..fed6bd9 100644 --- a/src/FluentAssertions.AspNetCore.Mvc/PartialViewResultAssertions.cs +++ b/src/FluentAssertions.AspNetCore.Mvc/PartialViewResultAssertions.cs @@ -14,7 +14,7 @@ namespace FluentAssertions.AspNetCore.Mvc public class PartialViewResultAssertions : ObjectAssertions { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The object to test assertion on public PartialViewResultAssertions(PartialViewResult subject) : base(subject) diff --git a/src/FluentAssertions.AspNetCore.Mvc/PhysicalFileResultAssertions.cs b/src/FluentAssertions.AspNetCore.Mvc/PhysicalFileResultAssertions.cs index 0566f91..71e8510 100644 --- a/src/FluentAssertions.AspNetCore.Mvc/PhysicalFileResultAssertions.cs +++ b/src/FluentAssertions.AspNetCore.Mvc/PhysicalFileResultAssertions.cs @@ -14,7 +14,7 @@ public class PhysicalFileResultAssertions : FileResultAssertionsBase - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The object to test assertion on public PhysicalFileResultAssertions(PhysicalFileResult subject) : base(subject) diff --git a/src/FluentAssertions.AspNetCore.Mvc/RedirectResultAssertions.cs b/src/FluentAssertions.AspNetCore.Mvc/RedirectResultAssertions.cs index a4fde55..30cb805 100644 --- a/src/FluentAssertions.AspNetCore.Mvc/RedirectResultAssertions.cs +++ b/src/FluentAssertions.AspNetCore.Mvc/RedirectResultAssertions.cs @@ -13,7 +13,7 @@ namespace FluentAssertions.AspNetCore.Mvc public class RedirectResultAssertions : ObjectAssertions { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// public RedirectResultAssertions(RedirectResult subject) : base(subject) { } diff --git a/src/FluentAssertions.AspNetCore.Mvc/RedirectToActionResultAssertions.cs b/src/FluentAssertions.AspNetCore.Mvc/RedirectToActionResultAssertions.cs index e5f4bf6..ee22446 100644 --- a/src/FluentAssertions.AspNetCore.Mvc/RedirectToActionResultAssertions.cs +++ b/src/FluentAssertions.AspNetCore.Mvc/RedirectToActionResultAssertions.cs @@ -13,7 +13,7 @@ namespace FluentAssertions.AspNetCore.Mvc public class RedirectToActionResultAssertions : ObjectAssertions { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// public RedirectToActionResultAssertions(RedirectToActionResult subject) : base(subject) { } diff --git a/src/FluentAssertions.AspNetCore.Mvc/RedirectToRouteAssertions.cs b/src/FluentAssertions.AspNetCore.Mvc/RedirectToRouteAssertions.cs index 1d60fa0..d754f31 100644 --- a/src/FluentAssertions.AspNetCore.Mvc/RedirectToRouteAssertions.cs +++ b/src/FluentAssertions.AspNetCore.Mvc/RedirectToRouteAssertions.cs @@ -15,7 +15,7 @@ public class RedirectToRouteAssertions : ReferenceTypeAssertions - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// public RedirectToRouteAssertions(RedirectToRouteResult subject) { diff --git a/src/FluentAssertions.AspNetCore.Mvc/RouteDataAssertions.cs b/src/FluentAssertions.AspNetCore.Mvc/RouteDataAssertions.cs index 5e1d487..9d84cc9 100644 --- a/src/FluentAssertions.AspNetCore.Mvc/RouteDataAssertions.cs +++ b/src/FluentAssertions.AspNetCore.Mvc/RouteDataAssertions.cs @@ -12,7 +12,7 @@ namespace FluentAssertions.AspNetCore.Mvc public class RouteDataAssertions : ObjectAssertions { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// public RouteDataAssertions(RouteData subject) : base(subject) diff --git a/src/FluentAssertions.AspNetCore.Mvc/SignInResultAssertions.cs b/src/FluentAssertions.AspNetCore.Mvc/SignInResultAssertions.cs index 3c77bfe..26e233a 100644 --- a/src/FluentAssertions.AspNetCore.Mvc/SignInResultAssertions.cs +++ b/src/FluentAssertions.AspNetCore.Mvc/SignInResultAssertions.cs @@ -18,7 +18,7 @@ public class SignInResultAssertions : ObjectAssertions #region Public Constructors /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The object to test assertion on public SignInResultAssertions(object subject) : base(subject) diff --git a/src/FluentAssertions.AspNetCore.Mvc/SignOutResultAssertions.cs b/src/FluentAssertions.AspNetCore.Mvc/SignOutResultAssertions.cs index 815ca4d..dbc6ca0 100644 --- a/src/FluentAssertions.AspNetCore.Mvc/SignOutResultAssertions.cs +++ b/src/FluentAssertions.AspNetCore.Mvc/SignOutResultAssertions.cs @@ -18,7 +18,7 @@ public class SignOutResultAssertions : ObjectAssertions #region Public Constructors /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The object to test assertion on public SignOutResultAssertions(object subject) : base(subject) diff --git a/src/FluentAssertions.AspNetCore.Mvc/StatusCodeResultAssertions.cs b/src/FluentAssertions.AspNetCore.Mvc/StatusCodeResultAssertions.cs index 948c114..f8fdfd9 100644 --- a/src/FluentAssertions.AspNetCore.Mvc/StatusCodeResultAssertions.cs +++ b/src/FluentAssertions.AspNetCore.Mvc/StatusCodeResultAssertions.cs @@ -12,7 +12,7 @@ namespace FluentAssertions.AspNetCore.Mvc public class StatusCodeResultAssertions : ObjectAssertions { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The object to test assertion on public StatusCodeResultAssertions(StatusCodeResult subject) : base(subject) diff --git a/src/FluentAssertions.AspNetCore.Mvc/ViewResultAssertions.cs b/src/FluentAssertions.AspNetCore.Mvc/ViewResultAssertions.cs index ce366a0..f06c2e5 100644 --- a/src/FluentAssertions.AspNetCore.Mvc/ViewResultAssertions.cs +++ b/src/FluentAssertions.AspNetCore.Mvc/ViewResultAssertions.cs @@ -15,7 +15,7 @@ public class ViewResultAssertions : ObjectAssertions #region Public Constructors /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The object to test assertion on public ViewResultAssertions(ViewResult subject) : base(subject) diff --git a/src/FluentAssertions.AspNetCore.Mvc/VirtualFileResultAssertions.cs b/src/FluentAssertions.AspNetCore.Mvc/VirtualFileResultAssertions.cs index 7d21b59..04e33b2 100644 --- a/src/FluentAssertions.AspNetCore.Mvc/VirtualFileResultAssertions.cs +++ b/src/FluentAssertions.AspNetCore.Mvc/VirtualFileResultAssertions.cs @@ -12,7 +12,7 @@ public class VirtualFileResultAssertions : FileResultAssertionsBase - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The object to test assertion on public VirtualFileResultAssertions(VirtualFileResult subject) : base(subject) diff --git a/tests/FluentAssertions.AspNetCore.Mvc.Tests/ActionResultAssertionsOfTValue_Tests.cs b/tests/FluentAssertions.AspNetCore.Mvc.Tests/ActionResultAssertionsOfTValue_Tests.cs new file mode 100644 index 0000000..0614f45 --- /dev/null +++ b/tests/FluentAssertions.AspNetCore.Mvc.Tests/ActionResultAssertionsOfTValue_Tests.cs @@ -0,0 +1,77 @@ +using FluentAssertions.Mvc.Tests.Helpers; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.Infrastructure; +using System; +using Xunit; + +namespace FluentAssertions.AspNetCore.Mvc.Tests +{ + public class ActionResultAssertionsOfTValue_Tests + { + public const string Reason = FailureMessageHelper.Reason; + public readonly static object[] ReasonArgs = FailureMessageHelper.ReasonArgs; + + #region Public Methods + + [Fact] + public void Result_GivenActionResultOfTValue_ShouldReturnSameValue() + { + var actionResult = new BadRequestResult(); + var result = new ActionResult(actionResult); + + result.Should().Result.Should().BeSameAs(actionResult); + } + + [Fact] + public void Value_GivenAValue_ShouldReturnSameValue() + { + var theValue = new object(); + var result = new ActionResult(theValue); + + result.Should().Value.Should().BeSameAs(theValue); + } + + [Fact] + public void BeConvertibleTo_CallingConvertResultsDifferentType_ShouldFail() + { + var result = new ActionResult(new BadRequestObjectResult(new object())); + var failureMessage = FailureMessageHelper.ExpectedContextToBeConvertible( + "result", typeof(ActionResult).FullName, typeof(BadRequestObjectResult).FullName); + + Action action = () => result.Should().BeConvertibleTo(Reason, ReasonArgs); + + action.Should().Throw() + .WithMessage(failureMessage); + + } + + [Fact] + public void BeConvertibleTo_CallingConvertResultsGoodType_ShouldPass() + { + var result = new ActionResult(new OkObjectResult(new object())); + + result.Should().BeConvertibleTo(Reason, ReasonArgs); + } + + [Fact] + public void BeConvertibleTo_WithNullResult_ShouldPass() + { + var result = new ActionResult(new object()); + + result.Should().BeConvertibleTo(Reason, ReasonArgs); + } + + [Fact] + public void BeConvertibleTo_ShouldBeTheConvertedObject() + { + OkObjectResult expectation = new OkObjectResult(new object()); + var result = new ActionResult(expectation); + + var actual = + result.Should().BeConvertibleTo(Reason, ReasonArgs).Which; + + actual.Should().BeSameAs(expectation); + } + #endregion Public Methods + } +} \ No newline at end of file diff --git a/tests/FluentAssertions.AspNetCore.Mvc.Tests/ChallengeResultAssertions_Tests.cs b/tests/FluentAssertions.AspNetCore.Mvc.Tests/ChallengeResultAssertions_Tests.cs index 746490a..f436521 100644 --- a/tests/FluentAssertions.AspNetCore.Mvc.Tests/ChallengeResultAssertions_Tests.cs +++ b/tests/FluentAssertions.AspNetCore.Mvc.Tests/ChallengeResultAssertions_Tests.cs @@ -30,27 +30,7 @@ public void WithAuthenticationProperties_GivenUnexpected_ShouldFail() var actualAuthenticationProperties = new AuthenticationProperties(); var expectedAuthenticationProperties = new AuthenticationProperties(); ActionResult result = new ChallengeResult(actualAuthenticationProperties); - var failureMessage = @"Expected ChallengeResult.AuthenticationProperties to be - -Microsoft.AspNetCore.Authentication.AuthenticationProperties -{ - AllowRefresh = - ExpiresUtc = - IsPersistent = False - IssuedUtc = - Items = {empty} - RedirectUri = -} because it is 10 but found - -Microsoft.AspNetCore.Authentication.AuthenticationProperties -{ - AllowRefresh = - ExpiresUtc = - IsPersistent = False - IssuedUtc = - Items = {empty} - RedirectUri = -}."; + var failureMessage = FailureMessageHelper.AuthenticationPropertiesExpectations(result); Action a = () => result.Should().BeChallengeResult().WithAuthenticationProperties(expectedAuthenticationProperties, Reason, ReasonArgs); @@ -336,8 +316,8 @@ public void WithAuthenticationSchemes_GivenExpected_ShouldPass() [Fact] public void WithAuthenticationSchemes_GivenUnexpected_ShouldFail() { - var actualAuthenticationSchemes = new List() { "one", "two", "three"}; - var expectedAuthenticationSchemes = new List { "three", "four", "five"}; + var actualAuthenticationSchemes = new List() { "one", "two", "three" }; + var expectedAuthenticationSchemes = new List { "three", "four", "five" }; ActionResult result = new ChallengeResult(actualAuthenticationSchemes); var failureMessage = string.Format(FailureMessages.CommonListsNotIdentical, "ChallengeResult.AuthenticationSchemes"); @@ -350,7 +330,7 @@ public void WithAuthenticationSchemes_GivenUnexpected_ShouldFail() public void ContainsScheme_GivenExpected_ShouldPass() { const string testScheme = "testScheme"; - var actualSchemes = new List {testScheme}; + var actualSchemes = new List { testScheme }; ActionResult result = new ChallengeResult(actualSchemes); result.Should().BeChallengeResult().ContainsScheme(testScheme); diff --git a/tests/FluentAssertions.AspNetCore.Mvc.Tests/ConvertToActionResultAssertions_Tests.cs b/tests/FluentAssertions.AspNetCore.Mvc.Tests/ConvertToActionResultAssertions_Tests.cs new file mode 100644 index 0000000..2b4ffb5 --- /dev/null +++ b/tests/FluentAssertions.AspNetCore.Mvc.Tests/ConvertToActionResultAssertions_Tests.cs @@ -0,0 +1,130 @@ +using FluentAssertions.Mvc.Tests.Helpers; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.Infrastructure; +using Moq; +using System; +using Xunit; + +namespace FluentAssertions.AspNetCore.Mvc.Tests +{ + public class ConvertToActionResultAssertions_Tests + { + public const string Reason = FailureMessageHelper.Reason; + public readonly static object[] ReasonArgs = FailureMessageHelper.ReasonArgs; + + [Fact] + public void BeActionResult_GivenActionResultOfTValue_ShouldPass() + { + var actionResult = new BadRequestResult(); + IConvertToActionResult result = new ActionResult(actionResult); + + result.Should().BeActionResult(); + } + + [Fact] + public void BeActionResult_GivenActionResultOfTValue_ShouldReturnActionResultAssertionsOfTValue() + { + var actionResult = new BadRequestResult(); + IConvertToActionResult subject = new ActionResult(actionResult); + + var result = subject.Should().BeActionResult(); + + result.Should().BeOfType>(); + } + + [Fact] + public void BeActionResult_GivenNull_ShouldFail() + { + IConvertToActionResult result = null; + var failureMessage = FailureMessageHelper.ExpectedContextTypeXButFoundNull( + "result", typeof(ActionResult)); + + Action action = () => result.Should().BeActionResult(Reason, ReasonArgs); + + action.Should().Throw() + .WithMessage(failureMessage); + } + + [Fact] + public void BeActionResult_GivenUnexpectedTValue_ShouldFail() + { + var actionResult = new BadRequestResult(); + IConvertToActionResult result = new ActionResult(actionResult); + + Action action = () => result.Should().BeActionResult(Reason, ReasonArgs); + + action.Should().Throw() + .WithMessage("Expected result to be ActionResult with type System.Int32 because it is 10 but was System.String."); + } + + [Fact] + public void BeActionResult_GivenUnexpectedType_ShouldFail() + { + var mock = new Mock(); + var result = mock.Object; + var failureMessage = FailureMessageHelper.ExpectedContextTypeXButFoundY( + "result", "Microsoft.AspNetCore.Mvc.ActionResult`1[System.Object]", result.GetType().FullName); + + Action action = () => result.Should().BeActionResult(Reason, ReasonArgs); + + action.Should().Throw() + .WithMessage(failureMessage); + } + + [Fact] + public void BeConvertibleTo_CallingConvertResultsNull_ShouldFail() + { + var mock = new Mock(); + mock.Setup(e => e.Convert()).Returns((IActionResult)null); + var result = mock.Object; + var failureMessage = FailureMessageHelper.ExpectedContextToBeConvertible( + "result", typeof(ActionResult).FullName, ""); + + Action action = () => result.Should().BeConvertibleTo(Reason, ReasonArgs); + + action.Should().Throw() + .WithMessage(failureMessage); + + } + + [Fact] + public void BeConvertibleTo_CallingConvertResultsDifferentType_ShouldFail() + { + var mock = new Mock(); + mock.Setup(e => e.Convert()).Returns(new BadRequestObjectResult(new object())); + var result = mock.Object; + var failureMessage = FailureMessageHelper.ExpectedContextToBeConvertible( + "result", typeof(ActionResult).FullName, typeof(BadRequestObjectResult).FullName); + + Action action = () => result.Should().BeConvertibleTo(Reason, ReasonArgs); + + action.Should().Throw() + .WithMessage(failureMessage); + + } + + [Fact] + public void BeConvertibleTo_CallingConvertResultsGoodType_ShouldPass() + { + var mock = new Mock(); + mock.Setup(e => e.Convert()).Returns(new OkObjectResult(new object())); + var result = mock.Object; + + result.Should().BeConvertibleTo(Reason, ReasonArgs); + } + + [Fact] + public void BeConvertibleTo_ShouldBeTheConvertedObject() + { + var mock = new Mock(); + var expectation = new OkObjectResult(new object()); + mock.Setup(e => e.Convert()).Returns(expectation); + var result = mock.Object; + + var actual = + result.Should().BeConvertibleTo(Reason, ReasonArgs).Which; + + actual.Should().BeSameAs(expectation); + } + } +} \ No newline at end of file diff --git a/tests/FluentAssertions.AspNetCore.Mvc.Tests/FluentAssertions.AspNetCore.Mvc.Tests.csproj b/tests/FluentAssertions.AspNetCore.Mvc.Tests/FluentAssertions.AspNetCore.Mvc.Tests.csproj index 20aca28..3db5316 100644 --- a/tests/FluentAssertions.AspNetCore.Mvc.Tests/FluentAssertions.AspNetCore.Mvc.Tests.csproj +++ b/tests/FluentAssertions.AspNetCore.Mvc.Tests/FluentAssertions.AspNetCore.Mvc.Tests.csproj @@ -1,7 +1,7 @@ - netcoreapp2.0 + netcoreapp2.1;netcoreapp3.0 false @@ -15,6 +15,10 @@ + + + + diff --git a/tests/FluentAssertions.AspNetCore.Mvc.Tests/ForbidResultAssertions_Tests.cs b/tests/FluentAssertions.AspNetCore.Mvc.Tests/ForbidResultAssertions_Tests.cs index bcbe7b2..34e0770 100644 --- a/tests/FluentAssertions.AspNetCore.Mvc.Tests/ForbidResultAssertions_Tests.cs +++ b/tests/FluentAssertions.AspNetCore.Mvc.Tests/ForbidResultAssertions_Tests.cs @@ -30,27 +30,7 @@ public void WithAuthenticationProperties_GivenUnexpected_ShouldFail() var actualAuthenticationProperties = new AuthenticationProperties(); var expectedAuthenticationProperties = new AuthenticationProperties(); ActionResult result = new ForbidResult(actualAuthenticationProperties); - var failureMessage = @"Expected ForbidResult.AuthenticationProperties to be - -Microsoft.AspNetCore.Authentication.AuthenticationProperties -{ - AllowRefresh = - ExpiresUtc = - IsPersistent = False - IssuedUtc = - Items = {empty} - RedirectUri = -} because it is 10 but found - -Microsoft.AspNetCore.Authentication.AuthenticationProperties -{ - AllowRefresh = - ExpiresUtc = - IsPersistent = False - IssuedUtc = - Items = {empty} - RedirectUri = -}."; + var failureMessage = FailureMessageHelper.AuthenticationPropertiesExpectations(result); Action a = () => result.Should().BeForbidResult().WithAuthenticationProperties(expectedAuthenticationProperties, Reason, ReasonArgs); diff --git a/tests/FluentAssertions.AspNetCore.Mvc.Tests/Helpers/FailureMessageHelper.cs b/tests/FluentAssertions.AspNetCore.Mvc.Tests/Helpers/FailureMessageHelper.cs index bb1e6d8..da483f7 100644 --- a/tests/FluentAssertions.AspNetCore.Mvc.Tests/Helpers/FailureMessageHelper.cs +++ b/tests/FluentAssertions.AspNetCore.Mvc.Tests/Helpers/FailureMessageHelper.cs @@ -72,7 +72,12 @@ private static object ToString(DateTimeOffset? expected) internal static string ExpectedContextTypeXButFoundY(string context, Type expected, Type actual) { - return $"Expected {context} to be of type {expected.FullName} but was {actual.FullName}."; + return ExpectedContextTypeXButFoundY(context, expected.FullName, actual.FullName); + } + + internal static string ExpectedContextTypeXButFoundY(string context, string expected, string actual) + { + return $"Expected {context} to be of type {expected} but was {actual}."; } internal static string ExpectedContextTypeXButFoundNull(string context, Type expectedType) @@ -80,6 +85,11 @@ internal static string ExpectedContextTypeXButFoundNull(string context, Type exp return $"Expected {context} to be of type {expectedType}, but no value was supplied."; } + internal static string ExpectedContextToBeConvertible(string context, string expectedType, string actualType) + { + return $"Expected {context} to be convertible to {expectedType} but it was {actualType}."; + } + internal static string ExpectedContextContainValueAtKeyButFoundNull(string context, string value, string key) { return $"Expected {context} to contain value \"{value}\" at key \"{key}\" because it is 10, but it is ."; @@ -102,5 +112,33 @@ internal static string ExpectedContextContainValueAtKeyButKeyNotFound(string con ? new DateTimeOffset?(result) : new DateTimeOffset?(); } + + public static string AuthenticationPropertiesExpectations(object containingObject) + { + var failureMessage = @"Expected " + containingObject.GetType().Name + @".AuthenticationProperties to be + +Microsoft.AspNetCore.Authentication.AuthenticationProperties +{ + AllowRefresh = + ExpiresUtc = + IsPersistent = False + IssuedUtc = + Items = {empty} + Parameters = {empty} + RedirectUri = +} because it is 10 but found + +Microsoft.AspNetCore.Authentication.AuthenticationProperties +{ + AllowRefresh = + ExpiresUtc = + IsPersistent = False + IssuedUtc = + Items = {empty} + Parameters = {empty} + RedirectUri = +}."; + return failureMessage; + } } } diff --git a/tests/FluentAssertions.AspNetCore.Mvc.Tests/SignInResultAssertions_Tests.cs b/tests/FluentAssertions.AspNetCore.Mvc.Tests/SignInResultAssertions_Tests.cs index d2be777..8b73f60 100644 --- a/tests/FluentAssertions.AspNetCore.Mvc.Tests/SignInResultAssertions_Tests.cs +++ b/tests/FluentAssertions.AspNetCore.Mvc.Tests/SignInResultAssertions_Tests.cs @@ -33,27 +33,7 @@ public void WithAuthenticationProperties_GivenUnexpected_ShouldFail() var actualAuthenticationProperties = new AuthenticationProperties(); var expectedAuthenticationProperties = new AuthenticationProperties(); ActionResult result = new SignInResult(TestAuthenticationScheme, TestClaimsPrincipal, actualAuthenticationProperties); - var failureMessage = @"Expected SignInResult.AuthenticationProperties to be - -Microsoft.AspNetCore.Authentication.AuthenticationProperties -{ - AllowRefresh = - ExpiresUtc = - IsPersistent = False - IssuedUtc = - Items = {empty} - RedirectUri = -} because it is 10 but found - -Microsoft.AspNetCore.Authentication.AuthenticationProperties -{ - AllowRefresh = - ExpiresUtc = - IsPersistent = False - IssuedUtc = - Items = {empty} - RedirectUri = -}."; + var failureMessage = FailureMessageHelper.AuthenticationPropertiesExpectations(result); Action a = () => result.Should().BeSignInResult().WithAuthenticationProperties(expectedAuthenticationProperties, Reason, ReasonArgs); diff --git a/tests/FluentAssertions.AspNetCore.Mvc.Tests/SignOutResultAssertions_Tests.cs b/tests/FluentAssertions.AspNetCore.Mvc.Tests/SignOutResultAssertions_Tests.cs index bb3e1bf..4fa65d9 100644 --- a/tests/FluentAssertions.AspNetCore.Mvc.Tests/SignOutResultAssertions_Tests.cs +++ b/tests/FluentAssertions.AspNetCore.Mvc.Tests/SignOutResultAssertions_Tests.cs @@ -30,27 +30,7 @@ public void WithAuthenticationProperties_GivenUnexpected_ShouldFail() var actualAuthenticationProperties = new AuthenticationProperties(); var expectedAuthenticationProperties = new AuthenticationProperties(); ActionResult result = new SignOutResult(TestAuthenticationSchemes, actualAuthenticationProperties); - var failureMessage = @"Expected SignOutResult.AuthenticationProperties to be - -Microsoft.AspNetCore.Authentication.AuthenticationProperties -{ - AllowRefresh = - ExpiresUtc = - IsPersistent = False - IssuedUtc = - Items = {empty} - RedirectUri = -} because it is 10 but found - -Microsoft.AspNetCore.Authentication.AuthenticationProperties -{ - AllowRefresh = - ExpiresUtc = - IsPersistent = False - IssuedUtc = - Items = {empty} - RedirectUri = -}."; + var failureMessage = FailureMessageHelper.AuthenticationPropertiesExpectations(result); Action a = () => result.Should().BeSignOutResult().WithAuthenticationProperties(expectedAuthenticationProperties, Reason, ReasonArgs);