Skip to content

Commit 33eb44a

Browse files
authored
Merge pull request #360 - Should return ActionResult<T> enhancements
Should return ActionResult<T> enhancements
2 parents a2ee126 + a1a8a60 commit 33eb44a

File tree

93 files changed

+1741
-452
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

93 files changed

+1741
-452
lines changed

README.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,19 @@ Funds donated via both platforms are used for development and marketing purposes
103103

104104
Additionally, funds donated via [Patreon](https://www.patreon.com/ivaylokenov) (see the stretch goals) give me the freedom to add more features to the free `Lite` edition of the library.
105105

106+
## Main Features
107+
108+
- **Built-in service mock resolver** - register your mocks once, use them everywhere.
109+
- **Controller tests** - unit or integration tests for both MVC or API scenarios.
110+
- **View component tests** - validate your view logic as fast as possible.
111+
- **Route tests** - asserting route paths and model binding is as accessible as it can get.
112+
- **Pipeline tests** - from the web request to the returned response - the whole chain is validated.
113+
- **Simple and easy to learn fluent API** - takes no more than 20 minutes to learn the library.
114+
- **Strongly-typed assertions** - the fluent test chain is designed to be developer-friendly and helpful.
115+
- **Friendly error messages** - failed tests throw exceptions with detailed error reports.
116+
- **Isolated test scope** - each test is run in isolated scope, making asynchronous execution more than welcome.
117+
- **Built-in mocks** - in-memory database, authentication, authorization, session, caching, temp data, and more.
118+
106119
## Quick Start
107120

108121
To add **MyTested.AspNetCore.Mvc** to your solution, you must follow these simple steps:

src/MyTested.AspNetCore.Mvc.Abstractions/Builders/Base/BaseTestBuilderWithActionContext.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,13 +33,17 @@ private set
3333

3434
protected IAndTestBuilder Passing<TActionResult>(Action<TActionResult> assertions)
3535
{
36+
InvocationResultValidator.ValidateInvocationResultType<TActionResult>(this.TestContext);
37+
3638
assertions(this.TestContext.MethodResultAs<TActionResult>());
3739

3840
return new AndTestBuilder(this.TestContext);
3941
}
4042

4143
protected IAndTestBuilder Passing<TActionResult>(Func<TActionResult, bool> predicate)
4244
{
45+
InvocationResultValidator.ValidateInvocationResultType<TActionResult>(this.TestContext);
46+
4347
if (!predicate(this.TestContext.MethodResultAs<TActionResult>()))
4448
{
4549
throw new InvocationResultAssertionException(string.Format(

src/MyTested.AspNetCore.Mvc.Abstractions/Internal/Http/HttpContextMock.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ public HttpContextMock(IFeatureCollection features)
3838
this.httpContext = new DefaultHttpContext(features);
3939

4040
this.CustomRequest = this.httpRequest;
41-
this.httpResponse = this.httpResponse ?? new HttpResponseMock(this.httpContext);
41+
this.httpResponse ??= new HttpResponseMock(this.httpContext);
4242
}
4343

4444
/// <summary>

src/MyTested.AspNetCore.Mvc.Abstractions/Internal/PluginsContainer.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ public static void LoadPlugins(DependencyContext dependencyContext)
4545
var plugins = testFrameworkAssemblies
4646
.Select(l => Assembly
4747
.Load(new AssemblyName(l.Name))
48-
.GetType($"{testFrameworkName}.Plugins.{l.Name.Replace(testFrameworkName, string.Empty).Trim('.')}TestPlugin"))
48+
.GetType($"{testFrameworkName}.Plugins.{l.Name.Replace(testFrameworkName, string.Empty).Replace(".", string.Empty)}TestPlugin"))
4949
.Where(p => p != null)
5050
.ToArray();
5151

src/MyTested.AspNetCore.Mvc.Abstractions/Internal/Routing/MvcRouteResolver.cs

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
using Microsoft.AspNetCore.Mvc;
77
using Microsoft.AspNetCore.Mvc.Abstractions;
88
using Microsoft.AspNetCore.Mvc.Controllers;
9-
using Microsoft.AspNetCore.Mvc.Filters;
109
using Microsoft.AspNetCore.Mvc.Infrastructure;
1110
using Microsoft.AspNetCore.Routing;
1211
using Microsoft.Extensions.DependencyInjection;
@@ -49,8 +48,7 @@ public static ResolvedRouteContext Resolve(
4948
var actions = routeContext
5049
.RouteData
5150
.Routers
52-
.Where(r => r.GetType() == WebFramework.Internals.MvcAttributeRouteHandler)
53-
.FirstOrDefault()
51+
.FirstOrDefault(r => r.GetType() == WebFramework.Internals.MvcAttributeRouteHandler)
5452
?.Exposed()
5553
.Actions
5654
?? actionSelector.SelectCandidates(routeContext);
@@ -100,9 +98,10 @@ public static ResolvedRouteContext Resolve(
10098
var filters = actionDescriptor
10199
.FilterDescriptors
102100
.OrderByDescending(f => f.Order)
103-
.Select(f => $"{f.Filter.GetName()} ({f.Scope.ToFilterScopeName()})");
101+
.Select(f => $"{f.Filter.GetName()} ({f.Scope.ToFilterScopeName()})")
102+
.ToArray();
104103

105-
var filtersMessage = filters != null && filters.Any()
104+
var filtersMessage = filters.Any()
106105
? $"filters - {string.Join(", ", filters)}"
107106
: "filters";
108107

src/MyTested.AspNetCore.Mvc.Abstractions/Internal/TestContexts/ComponentTestContext.cs

Lines changed: 9 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -20,44 +20,13 @@ public abstract class ComponentTestContext : HttpTestContext
2020
private IEnumerable<object> methodAttributes;
2121
private object model;
2222

23-
public object Component
24-
{
25-
get
26-
{
27-
if (this.component == null)
28-
{
29-
this.component = this.ComponentConstructionDelegate();
30-
}
23+
public object Component => this.component ??= this.ComponentConstructionDelegate();
3124

32-
return this.component;
33-
}
34-
}
35-
36-
public IEnumerable<object> ComponentAttributes
37-
{
38-
get
39-
{
40-
if (this.componentAttributes == null)
41-
{
42-
this.componentAttributes = Reflection.GetCustomAttributes(this.Component);
43-
}
25+
public IEnumerable<object> ComponentAttributes
26+
=> this.componentAttributes ??= Reflection.GetCustomAttributes(this.Component);
4427

45-
return this.componentAttributes;
46-
}
47-
}
48-
49-
public IDictionary<Type, object> AggregatedDependencies
50-
{
51-
get
52-
{
53-
if (this.aggregatedDependencies == null)
54-
{
55-
this.aggregatedDependencies = new Dictionary<Type, object>();
56-
}
57-
58-
return this.aggregatedDependencies;
59-
}
60-
}
28+
public IDictionary<Type, object> AggregatedDependencies
29+
=> this.aggregatedDependencies ??= new Dictionary<Type, object>();
6130

6231
public string MethodName
6332
{
@@ -98,24 +67,11 @@ public object MethodResult
9867
{
9968
get => this.methodResult;
10069

101-
set
102-
{
103-
this.methodResult = this.ConvertMethodResult(value);
104-
}
70+
set => this.methodResult = this.ConvertMethodResult(value);
10571
}
10672

107-
public IEnumerable<object> MethodAttributes
108-
{
109-
get
110-
{
111-
if (this.methodAttributes == null)
112-
{
113-
this.methodAttributes = Reflection.GetCustomAttributes(this.Method);
114-
}
115-
116-
return this.methodAttributes;
117-
}
118-
}
73+
public IEnumerable<object> MethodAttributes
74+
=> this.methodAttributes ??= Reflection.GetCustomAttributes(this.Method);
11975

12076
public Exception CaughtException { get; set; }
12177

@@ -160,6 +116,6 @@ public void Apply<TMethodResult>(InvocationTestContext<TMethodResult> invocation
160116
this.CaughtException = invocationTestContext.CaughtException;
161117
}
162118

163-
protected virtual object ConvertMethodResult(object methodResult) => methodResult;
119+
protected virtual object ConvertMethodResult(object convertibleMethodResult) => convertibleMethodResult;
164120
}
165121
}

src/MyTested.AspNetCore.Mvc.Abstractions/Plugins/AbstractionsTestPlugin.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ public class AbstractionsTestPlugin : BaseTestPlugin, IServiceRegistrationPlugin
1313
public Action<IServiceCollection> ServiceRegistrationDelegate =>
1414
serviceCollection => serviceCollection.AddCoreTesting();
1515

16-
// Тhis call prepares all application conventions and fills the controller action descriptor cache.
16+
// This call prepares all application conventions and fills the controller action descriptor cache.
1717
public Action<IServiceProvider> InitializationDelegate => serviceProvider => serviceProvider.GetService<IControllerActionDescriptorCache>();
1818

1919
public object TryGetValue(Type type, ComponentTestContext testContext)
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
namespace MyTested.AspNetCore.Mvc.Utilities.Extensions
2+
{
3+
using System;
4+
using Internal.TestContexts;
5+
using Microsoft.AspNetCore.Mvc;
6+
7+
public static class ActionTestContextExtensions
8+
{
9+
private static readonly Type ActionResultGenericType = typeof(ActionResult<>);
10+
private static readonly Type ObjectResultType = typeof(ObjectResult);
11+
12+
public static ActionTestContext ConvertMethodResult(this ActionTestContext testContext)
13+
{
14+
var methodReturnType = testContext.Method.ReturnType;
15+
16+
if (Reflection.AreAssignableByGeneric(ActionResultGenericType, methodReturnType))
17+
{
18+
var methodResultType = testContext.MethodResult.GetType();
19+
20+
if (Reflection.AreSameTypes(ObjectResultType, methodResultType))
21+
{
22+
var objectResult = testContext.MethodResult as ObjectResult;
23+
24+
testContext.MethodResult = objectResult?.Value;
25+
}
26+
}
27+
28+
return testContext;
29+
}
30+
}
31+
}

src/MyTested.AspNetCore.Mvc.Abstractions/Utilities/Extensions/ObjectExtensions.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ public static T TryCastTo<T>(this object obj)
2020
}
2121
catch (InvalidCastException)
2222
{
23-
return default(T);
23+
return default;
2424
}
2525
}
2626

src/MyTested.AspNetCore.Mvc.Abstractions/Utilities/Extensions/TupleExtensions.cs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,15 @@ public static (string, string) GetTypeComparisonNames(this (Type Expected, Type
99
var expected = typeTuple.Expected;
1010
var actual = typeTuple.Actual;
1111

12-
var useFullName = expected?.Name == actual?.Name;
12+
var expectedName = expected.ToFriendlyTypeName();
13+
var actualName = actual.ToFriendlyTypeName();
1314

14-
return (expected.ToFriendlyTypeName(useFullName), actual.ToFriendlyTypeName(useFullName));
15+
if (expectedName == actualName)
16+
{
17+
return (expected.ToFriendlyTypeName(true), actual.ToFriendlyTypeName(true));
18+
}
19+
20+
return (expectedName, actualName);
1521
}
1622
}
1723
}

0 commit comments

Comments
 (0)