Controller Test problems - asp.net-mvc

I want to test a mvc controller. I'm using Moq to mock the services, but I don't know how to mock this.Request.Files["Attachement1"] and this.Server.MapPath("~/Temp") ("this" is the controller)
I tried to create a new Mock<HttpRequestBase>(); but this.Request doesn't have a setter.
Help me please with an advise. Thanks

I had a similar problem as your - I used the set of fake classes from Stephen Walther's blog.
Asp.Net MVC Tips - Faking the Controller Context
I had to modify some of the classes slightly but it should do what you want and it's definitely a lot easier to setup than having to mock the entire context every time.

For the folder name resolutions such as: this.Server.MapPath("~/Temp") I use public properties, and getter returns this so I can easily test it.
For the Request.Files, I prefer using FormCollection dictionary

If you have a look at the TestHelper in the MVCContrib project, it can easily be extended to Mock other bits of the Http elements. (It has some Request elements already to use as a template.)
Kindness,
Dan

Related

Working with helpers in the dotnetcore DI world

I like to have static helper classes in my apps for common plumbing stuff, like checking roles, or Razor Html helpers and stuff... but how do you do this in the DI world?
Like lets say I want an extension helper to check if a user is an admin
public static async Task<bool> IsAdmin(this ApplicationUser user)
...
if(user.IsAdmin()){...}
So pre-core I could just ignore DI and create a UserManager all inside IsAdmin do whatever I need to do. But now is there no way to get the UserManager in these helpers to just use? The only way I can see is to inject it into the Controller, then pass along into the method (which I find ugly). Then there's the issue of trying to do user.IsAdmin() in the Razor view, would I need to add the UserManager to the ViewData collection to get it into the view markup?
Am I just missing something here?
Thanks,
Steve
First of all if you just asked how to use static class with di, i would say that your question is duplicate of How to use DI inside a static Method in Asp.net Core rc1
But as i see, you have some other question?
But now is there no way to get the UserManager in these helpers to
just use?
Yes there is a way : Service Locator pattern. But it is an anti pattern (see this article) . As far as possible you should avoid to use this pattern. Also see discussion in github.
The only way I can see is to inject it into the Controller, then pass
along into the method (which I find ugly)
I think this way is better than you want. I would prefer this.
Then there's the issue of trying to do user.IsAdmin() in the Razor
view, would I need to add the UserManager to the ViewData collection
to get it into the view markup?
In Aspnet core you can inject a dependency into a view, so you don't need to use ViewData. Simply you can inject UserManager into your view and then pass it as parameter to method. Take a look at official docs
Well you are talking about a cross-cutting concern here and one way how I've seen cross cutting concerns solved in ASP.NET Core MVC is with attributes (like [Authorize] for example). Which I think is an elegant solution.
So, if I understand your question correctly I think you can solve this with an Action Filter. Damien Bod described a few days ago how to use ActionFilters: https://damienbod.com/2016/09/09/asp-net-core-action-arguments-validation-using-an-actionfilter/.
So in short, you inherit from ActionFilterAttribute and make your own curstom filter called MyCustomFilter or whatever. Have this MyCustomFilter request UserManager in its constructor via DI. Then above any action method in a controller you say:
ServiceFilter[typeof(MyCustomFilter)]
And in MyCustomFilter you ofcourse have logic to check if User is IsAdmin and then take action accordingly.
Now, I've always used Microsoft's Unity to handle cross cutting concerns via interception (you can read more about that here: https://dannyvanderkraan.wordpress.com/2015/09/30/real-world-example-of-adding-auditing-with-dependency-injections-interception/. But last time I checked there is no Unity container for asp.net core yet. But this guy has a great article about porting it to core: https://dzimchuk.net/post/bring-your-own-di-container-to-aspnet-5-unity. I would really like my Interception back! Very elegant solution to cross cutting concerns. They are working on it though: https://github.com/unitycontainer/unity/issues/66. Fingers crossed...

How does asp.net mvc convert post data to a model?

I am extracting some code from a project outside of the MVC-area to a HttpHandler while reusing as much code as possible.
To make things easy I would like to convert the posted data to the same Model that was already used in the Controller.
So, now I am looking for the logic MVC is using under the covers to magically convert posted data to a Model, but after several decompile attempts I am unable to find this.
Could anyone point me in the right direction?
Thanks!
SaphuA: Basically, I want to input the HttpContext and the type of the target Model and have the helper method do the rest of the mapping.
As Eric says, just extent the DefaultModelBinder.cs
The default model binder is here
The model binder is called from the controller action invoker
The overall flow is nicely outlined here so just scroll to check where the controller action invoker is called.
If you want to spin up some unit tests around your implementation of the binder, check out the MVC source tests
Using the github mirror because codeplex does not support line number linking yet :-)
HTH
I think this article explains it pretty well.
In a nutshell there is a DefaultModelBinder class that handles the conversion of POST data into a model object. You can even replace that model binder with your own if you like by extending it or implementing IModelBinder then registering your model binder with MVC in your Application_Start method.

Sending a parameter to the controller in ASP MVC 2

I am writing an area for administering several subsites, almost all of the functionality will be the same across each site (add/edit/remove pages etc) and my repository on instantiation takes the SiteIdentity so all the data access methods are agnostic in relation to this. The problem I have at the moment is trying to make my action methods also agnostic.
The URL pattern I want to use is along the lines of:
"ExternalSite/{identity}/{controller}/{action}/{id}"
A naive approach is to have each action take the identity parameter, but this means having to pass this in to my repository on each action as well as include it in the ViewData for a couple of UI elements. I'd much rather have this something that happens once in the controller, such as in its constructor.
What is the best way to do this? Currently the best I can come up with is trying to find and cast identity from the RouteData dictionary but part of me feels like there should be a more elegant solution.
It sounds like you want to use OnActionExecuting or a Custom ModelBinder to do that logic each time you have a specific parameter name (also known as a RouteData dictionary key).
Creating a custom modelbinder in ASP.NET MVC
Creating an OnActionExecuting method in ASP.NET MVC, Doing Serverside tracking in ASP.NET MVC
You have access to your route values in Request.RequestContext.RouteData, so you can make base controller and public property SiteIdentity, in such case you can access it from all actions in all inherited controllers.

ASP.NET MVC Unit Testing and HttpContext

ASP.NET MVC has come a long way (compared to webforms) in becoming a unit testable framework. However, we are often faced with some remaining pigs like FormsAuthentication, which I usually wrap in some type of UserSession object to keep it clean and testable. The other day I realized I was using Server.MapPath in my controller action, and while things like MvcContrib make it easy(ish) to mock the current request, I found myself going down the path of creating a FileSystemService class to wrap operations with the file system. The benefits are that you get a tight API exposing just the methods you need, and it's easy to stub/mock in tests. The downside is that it is yet another constructor dependency.
What does the SO community thing about this situation? Where do you draw the line when trying to make your controllers as light and flexible as possible?
I would generally draw the line at the point where you need to actually get something done (i.e. somebody wants to use the code you're writing). If it takes another half a day to write your service etc. and your deadline is looming, then some things are best left...
Bear in mind that ASP.NET MVC has already replaced the sealed HttpContext with more test-friendly HttpContextBase, which you can mock to provide your tests with your chosen implementations of Server.*, Request.*, Response.*, etc.
You just need to set the controller's ControllerContext to provide your test context:
controllerUnderTest.ControllerContext = new ControllerContext( testHttpContext, new RouteData(), controllerUnderTest );
This is a great question! I do the same thing you do - wrap HttpContext dependencies so they can be visible, inverted, and mocked.
I draw the controller line at validating the post values and saving or passing the call off to a task layer. So if I have to validate, save some data, send an email to marketing if a preferred customer makes a purchase, log something, etc., that's too much. I'd put that in a task layer and have my controller call that task layer. Most of the dependencies then float to the task layer constructors and come out of the controller.
My general rule of thumb is this: If you were asked to create a Silverlight or some other UI on your app, would you be totally rewriting it? Controllers should stick to presentation logic. If you can scrape off the presentation layer and put another one on, I think that gives you the best future proofing.
Of course, where you draw the line is up to you and your app. See Who Can Help Me, which is based on Sharp Architecture, for more about the task layer and skinny controllers.
There are also these wrappers around things like HttpContext to make testing easier:
http://www.codethinked.com/post/2008/12/04/Using-SystemWebAbstractions-in-Your-WebForms-Apps.aspx
So to sum it up:
Get System.Web.Abstractions
Use HttpContext base as a parameter, not HttpContext:
public void SomeMethod(HttpContextBase context)
{
}
Call the method wrapping your real http context
HttpContextBase contextBase = new HttpContextWrapper(context);
SomeMethod(contextBase);
You can provide fakes like so (example using Moq):
var context = new Mock<HttpContextBase>();
context.Setup(x => x.Request.QueryString).Returns(new NameValueCollection { { "Id", "5" } });

lessons learned or mistakes made when using asp.net mvc

what are your top lessons learned when starting asp.net mvc that you would highlight to someone starting out so they can avoid these mistakes?
Use Html.Encode() everywhere you print data, unless you have a very good reason to not do so, so you don't have to worry about XSS
Don't hardcode routes into your views or javascripts - they're going to change at some point, use Url.Action() instead
Don't be afraid of using partial views
MVC is no silver bullet, first evaluate if it's indeed the best tool of choice for solving your problem.
Don't forget the "Unit Tests" part of the pattern.
Try to always use a ViewModel to pass data between the Controller and the View.
You may think you don't need one, you can just pass your model around, but suddenly you need a list box with several options for editing a model, or displaying a message (not validation message) and you start adding items to the ViewData, with magic strings as keys, making the app harder to maintain.
There are also some security issues that you solve with a ViewModel.
For instance:
class user:
int id
string name
string email
string username
string password
Your view let's the user change his name and email and posts to the action
public ActionResult Edit(User user)
{
--persist data
}
Someone could tamper your form and post a new password and username and you will need to be very careful with the DefaultBinder behavior.
Now, if you use a ViewModel like:
class userEditViewModel:
int id
string name
string email
The problem is gone.
Whenever it is possible make your view typed
Avoid logic in your views
stay away from the HttpContext
Get Steve Sandersons Pro ASP.NET MVC Framework
Debug into the Sourcecode
If you make a Controller method with a different parameter name from id for a single parameter method, you have to make a new route. Just bite the bullet and use id (it doesn't care about the type) and explain it in the comments.
Makes sure you name your parameters with RedirectToAction :
return RedirectToAction("DonateToCharity", new { id = 1000 });
You lose your ViewData when you RedirectToAction.
Put javascript in seperate files, not into the view page
name of the controller :)
unit test Pattern
Don't use the Forms collection, use model binding.
Try not to use ViewData, create a ViewModel.
If you have a loop or an if in your View, write an HTML helper.
Kindness,
Dan
Don't let your controller become a fat one and do too much work. I've seen 1000+ line controllers in the past and it just becomes an absolute nightmare to understand what's going.
Utilise unit testing for your controllers to ensure that dependencies are kept under control and that your code is testable.
Don't get drawn into letting jQuery and fancy clientscript define the behaviour of your application, try and use it as sparingly as you can and let it enhance your application instead.
Use partial views and HTML helpers whenever possible to ensure that your Views do not become unwieldy and a maintenance nightmare.
Use a ViewModel whenever possible.
Use a dependency injection framework to handle your dependencies (MvcContrib has several controller factories, though it's simple enough to roll your own).
Use a different controller for every section of your site (e.g., Home, Account)
Learn how to use ViewData and TempData
Learn what's the use of RenderPartial

Resources