Created
August 21, 2020 03:28
-
-
Save bronumski/f85cb9fa3e2f86002e84da081405b6b0 to your computer and use it in GitHub Desktop.
ASP.NET MVC 5 Unit Testing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
abstract class SpecsForController<TSut, TResult> where TSut : ControllerBase where TResult : ActionResult | |
{ | |
private readonly NSubstituteMockingKernel kernel = new NSubstituteMockingKernel(); | |
private TSut Subject { get; set; } | |
protected TResult Result { get; private set; } | |
protected HttpContextBase HttpContext => Subject.ControllerContext.HttpContext; | |
protected virtual void Arrange(ControllerContext context) { } | |
protected abstract Expression<Action<TSut>> Act { get; } | |
protected TService ResolveDependency<TService>() => kernel.Get<TService>(); | |
protected void RegisterDependency<TDependency>(TDependency dependency) | |
=> kernel.Bind<TDependency>().ToConstant(dependency); | |
protected virtual HttpMethod Method => HttpMethod.Get; | |
[OneTimeSetUp] | |
public void SetUp() | |
{ | |
var httpContextBase = Substitute.For<HttpContextBase>(); | |
httpContextBase.Request.QueryString.Returns(new NameValueCollection()); | |
httpContextBase.Request.Cookies.Returns(new HttpCookieCollection()); | |
httpContextBase.Request.HttpMethod.Returns(Method.Method); | |
httpContextBase.Request.IsAuthenticated.Returns(x => httpContextBase.User?.Identity.IsAuthenticated == true); | |
var controllerContext = new ControllerContext | |
{ | |
RouteData = new RouteData(), | |
HttpContext = httpContextBase, | |
}; | |
System.Web.HttpContext.Current = new HttpContext( | |
new HttpRequest("", "http://tempuri.org", "") | |
{ | |
Browser = new HttpBrowserCapabilities | |
{ | |
Capabilities = new Dictionary<string, string> { { "supportsEmptyStringInCookieValue", "false" }, { "cookies", "false" } } | |
} | |
}, | |
new HttpResponse(new StringWriter())); | |
kernel.Bind<ILogPerformanceRepository>().To<DummyLogPerformanceRepository>(); | |
Arrange(controllerContext); | |
var controller = kernel.Get<TSut>(); | |
controllerContext.Controller = controller; | |
controller.ControllerContext = controllerContext; | |
Subject = controller; | |
var actionInvoker = new ControllerSpecActionInvoker<TResult>(Act.Body); | |
var methodName = ExtractActionName(Act.Body); | |
actionInvoker.InvokeAction(Subject.ControllerContext, methodName); | |
Result = actionInvoker.Result; | |
} | |
private static string ExtractActionName(Expression body) | |
{ | |
switch (body) | |
{ | |
case MethodCallExpression e: return e.Method.Name; | |
case UnaryExpression e: return ExtractActionName(e.Operand); | |
default: throw new Exception("Cannot determine action from action"); | |
} | |
} | |
~SpecsForController() => kernel.Dispose(); | |
class DummyLogPerformanceRepository : ILogPerformanceRepository | |
{ | |
public void LogToDatabase(MethodPerformance methodPerformance) | |
{ | |
throw new NotImplementedException(); | |
} | |
public T LogPerformanceForMethod<T>(Expression<Func<T>> callbackExpression) => callbackExpression.Compile()(); | |
} | |
class ControllerSpecActionInvoker<TResult> : ControllerActionInvoker where TResult : ActionResult | |
{ | |
private readonly Expression body; | |
public ControllerSpecActionInvoker(Expression body) | |
{ | |
this.body = body; | |
} | |
public TResult Result { get; private set; } | |
protected override void InvokeActionResult(ControllerContext controllerContext, ActionResult actionResult) | |
=> Result = actionResult as TResult; | |
protected override IDictionary<string, object> GetParameterValues(ControllerContext controllerContext, ActionDescriptor actionDescriptor) | |
{ | |
if (body is MethodCallExpression methodCall) | |
{ | |
return methodCall.Method.GetParameters() | |
.Zip(methodCall.Arguments.Select(GetArgumentAsConstant), (param, arg) => new { param.Name, Value = ChangeType(arg.Value, param.ParameterType) }) | |
.ToDictionary(item => item.Name, item => item.Value); | |
} | |
return base.GetParameterValues(controllerContext, actionDescriptor); | |
} | |
private ConstantExpression GetArgumentAsConstant(Expression exp) | |
{ | |
switch (exp) | |
{ | |
case ConstantExpression constExp: | |
return constExp; | |
case UnaryExpression uranExp: | |
return GetArgumentAsConstant(uranExp.Operand); | |
} | |
throw new NotSupportedException($"Cannot handle expression of type '{exp.GetType()}'"); | |
} | |
private static object ChangeType(object value, Type conversion) | |
{ | |
var t = conversion; | |
if (!t.IsGenericType || t.GetGenericTypeDefinition() != typeof(Nullable<>)) return Convert.ChangeType(value, t); | |
if (value == null) return null; | |
t = Nullable.GetUnderlyingType(t); | |
return Convert.ChangeType(value, t); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment