Unit testing with ASP.NET MVC model binders - c#

I was wondering the best practice for unit-testing controller actions that utilize model binding.
[AcceptVerbs(HttpVerbs.Get)]
public ActionResult AddProduct(Product product)
{
}
I was wondering how you invoke the controller's method for unit testing. If you try something like this...
public void Catalog_AddProduct()
{
CatalogController controller = new CatalogController();
// some mocking for controller context, setting form values etc...
controller.AddProduct(// ?);
}
Some might suggest removing Product as a parameter but, I also have another AddProduct controller action that is used for just HTTP-Gets. The only solution I could think of is maybe accepting a namevalue collection (form data) and just using UpdateModel/TryUpdateModel.
I also want to test the model binding itself is working properly so I'd like to put the responsibility of creating the new product to the model binder.

I'm not sure I understand the problem, why can't you just do this:
[TestMethod()]
public void AddProductTest()
{
CatalogController target = new CatalogController(/*testing variables*/);
target.AddProduct(new Product { /* product details for testing */ });
// Test the results
}
Though I think perhaps I'm not understanding the problem. Using the form post variables is a good approach however, this will work really well when you need to do edits of the product and after a while you might find it's much easier to have all your actions take the form post variables and update your model. One thing worth pointing out with the TryUpdateModel and UpdateModel is that we have run into a bug with the Entity Framework, if you try to update an entitiy framework model that is complex it can sometimes throw exceptions. But it's really easy to write your own model updater as we've done.
EDIT:
I'm not sure you'll be able to, or that you need to, test the model binding itself. The model binding is part of the MVC framework and outside the scope of the test for the controller, I wouldn't concern myself with it and assume that it will work within the context of your test.
If you really need to test the model binding, the only way that I know is to pass in the form post variables and then use the TryUpdateModel method.

Now you just provided a ValueProvider to the controller.

Related

Is it simple to overpost an Edit controller even though you use a ViewModel in ASP.NET MVC?

I've learned to use ViewModels to protect my controllers from overposting attacks. However, take this example I see a lot:
[HttpPost]
public ActionResult Edit(ViewModel viewModel)
{
if (ModelState.IsValid)
{
var dbModel = db.Models.Find(viewModel.Id);
// ... bind viewModel properties to dbModel... but don't bind Id
db.Entry(dbModel).State = EntityState.Modified;
db.SaveChanges();
}
}
Yes, you are not binding the Id... but can't you overpost viewModel.Id to Find and change a different dbModel than intended? If so, is there a simple way to prevent this?
The issue of over posting was addressed in Scott`s post here. To quote:
How do we fix the problem? Well, a few ways. You can mark the property
as [ReadOnly]. More commonly, you can use a BindAttribute on the
method parameters and just include (whitelist) the properties you want
to allow for binding:
public async Task<IActionResult>> Create([Bind("First,Last")] Person person)
Or, the correct answer.
Don't let models that look like this get anywhere near the user. This
is the case for ViewModels. Make a model that looks like the View.
Then do the work. You can make the work easier with something like
AutoMapper.
Some folks find ViewModels to be too cumbersome for basic
stuff. That's valid. There are those that are "All ViewModels All The
Time," but I'm more practical. Use what works, use what's appropriate,
but know what's happening underneath so you don't get some
scriptkiddie overposting to your app and a bit getting flipped in your
Model as a side effect.
That's why it's good practice to add the id as a parameter to the action (and checking to see if it's the same as the id in the model). This, combined with an anti-forgery token should normally keep you safe.
Please also have a look at:
- Parameter Binding in ASP.NET Web API
- Preventing mass assignment or over posting in ASP.NET Core

Can you validate a model instance using properties from its controller?

Supposing I had, by way of demonstration, a controller that looked like this:
public class ProjectController : Controller
{
private IProjectRepository projectRepository;
public ProjectController()
{
DBContext context = new DBContext();
this.projectRepository = new ProjectRepository(context);
}
public ActionResult Create(Project project)
{
if (ModelState.IsValid)
{
// do whatever
}
else
{
return View(project);
}
}
}
And suppose that this was the controller for a model that looked like this:
public class Project : IValidatableObject
{
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
// this is where I would like to add code
}
}
My question is: is there really no way in the validation code to reference the projectRepository object on the controller? Yes, I could technically add quasi-validation to the Create() function on the controller, do the check there, and add errors directly - but in the real case, there are a number of actions that would all perform the same validation, which is really tied to the model you're trying to create (or edit, or copy, or whatever else). But it's tied to the model and to other existing instances of the same model, which only the controller knows how to query for. Is there any way to get around that?
(The goal is to check that the current model object under validation isn't the same as one that exists already; I'm open to other suggestions of how to do that as well, it just seemed like it clearly should be a job for standard validation code, either using IValidatableObject or using a ValidationAttribute. But I am not an expert in .net MVC validation, or for that matter, .net MVC at all.)
Thanks!
IValidatableObject belongs to the DataAnnotations namespace. To my mind, Data Annotations are great for input validation. Where they start to fall down is when you start applying complex business rules where your domain model's validity depends on the state of other domain models.
When that happens, introduce a service layer. Put all your business rules in there, and allow the service to mediate the conversation between your models. At the end of the day, a service is supposed to be the interface with which you communicate with your model.
This is where I usually say to myself, "Hey, your app has now reached the 'medium-complexity' stage"! :)
An older but still relevant tutorial can be found here: http://www.asp.net/mvc/tutorials/older-versions/models-(data)/validating-with-a-service-layer-cs
IMO, there is a bit of a convention issue at play. The model that the controller returns to the client is a ViewModel, not an entity. This comes into play when thinking about which objects have knowledge of dependent objects.
The repository deals with models (entities), and the controller deals with ViewModels. ViewModels are really just a bunch of data and formatting, so set-level validations don't make sense on a ViewModel.
And really want the business-layer or repository to perform set-level validation, not the model itself. You could set a reference to the repository on the model when it is created and have the model call the repository for set-level validation. But this becomes problematic when you want to clone or deserialize the entity.
By the way, EntityFramework solves these problems by allowing you to Attach a disconnected entity. You might want to use EF instead of the repository pattern.
But for you immediate issue, I would not try to perform set-level validation from within the entity or viewmodel.
You might be better of putting your validation in the service layer
The online scaffolding tool CamoteQ generate its model validation code this way, which is a good reference for self-study.

Need to access a variable from all Views

On the site I'm working on, there are users with different permissions on the site.
Given the schedule ID and employee ID that we're currently looking at, we can get their role-specific permissions.
Right now, our BaseModel has a property that properly accesses the DB and grabs this info.
For all views that pass a model to the view, everything runs fine.
The problem lies in Controller Methods where no model is passed. In a few views, all they're supplied is a few ViewBag entries, and work fine.
However, I /need/ the CurrentPermissions property in those pages nonetheless, for the layout. Whether or not the permissions have one boolean value set true/false, something may/may not be displayed/populated.
So, my option seem to be:
Somehow throw my CurrentPermissions into a ViewBag entry for all views, and access them through that instead of the base model.
I'm not sure how to do this. I've seen people using OnActionExecuting, but that fails since my connection to TransactionManager is not yet set up at that point.
Somehow throw just the BaseModel into those views that don't currently pass a model. I'm refraining from this as much as possible. I'm not sure how I would go about doing such, but it seems like that would over-complicate the situation.
How can I go about pushing this CurrentPermissions object (generated from a call to my TransactionManager) to every view (specifically, the Layouts!)
Your approach is what we use in out projects... and we use this approach to systematically remove the use of ViewBag changing it to ViewModels.
Other approach we have used (for UserPreferences in my case) is adding an ActionFilter that ends including the preference in the ViewBag. You decorate the actions needing it with [IncludePreferences] in my case (that is the name of my filter attribute.
EDIT ActionFilter:
public class IncludePreferencesAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
var controller = filterContext.Controller as BaseController;
// IController is not necessarily a Controller
if (controller != null)
{
//I have my preferences in the BaseController
//and cached but here you can query the DB
controller.ViewBag.MyPreferences = controller.TenantPreferences;
}
}
}
In you action you decorate it using [IncludePreferences]
As a temporary solution, I'm doing the following at the top of my Layout:
#{ OurModel.SupervisorRestriction CurrentSupervisorRestrictions = ViewBag.CurrentSupervisorRestrictions ?? Model.CurrentSupervisorRestrictions; }
This way, if we're passing in an object then it works just fine. Otherwise, I'll directly pass in a ViewBag.CurrentSupervisorRestrictions from the controller. There are only a few cases, so it's not that bad.
Better suggestions would be great, though.

Mocking HttpContext in MVC **Outside** of the Controller

Scenario
We're developing a new MVC web project and we're trying to adhere to the Skinny Controller pattern as described in this article http://codebetter.com/iancooper/2008/12/03/the-fat-controller/
As part of one of our actions we are retrieving some navigation data (Menu structure) from the Cache.
Problem
I order to maintain the skinny controller pattern we'd like to have the cache check call in the ViewModel, which we have tried and we know works, using the following code.
var cachedCategories = (List<Category>)HttpContext.Current.Cache["Categories"];
if (cachedCategories == null) {
cachedCategories = _service.GetCategories().ToList<Category>();
HttpContext.Current.Cache["Categories"] = cachedCategories;
}
However when it comes to unit testing it we hit a problem. Since we are not directly passing the HttpContext into the ViewModel we have no idea how to go about mocking the HttpContext.
We're using Moq and while we have some options (one is to pass the context from the controller to the viewmodel at instantiation) those options require changing the code purely to make the tests work.
Does anyone have any suggestions?
mock the HttpContext is a huge work as it is one of the biggest object you will see in all your life so probably is better don't mock it.(http://volaresystems.com/Blog/post/Dont-mock-HttpContext.aspx)
Anyway you could use the one in MVCcontrib (http://www.codeplex.com/mvcContrib) the file MvcMockHelps shows how it is done.
Ultimately we choose to modify our code to allow for easier testing.
We accomplished this by passing the HttpContext to the ViewModel at instantiation as I mentioned in my original question.

MVC 3 - How is this ever going to work?

I have made this post over a year ago, and I think it makes sense to update it as it's getting quite a few views.
I'm either missing something out or Microsoft has really messed up MVC. I worked on Java MVC projects and they were clean and simple. This is however a complete mess IMO. Examples online such as NerdDinner and projects discussed on ASP.Net are too basic, hence why they "simply" work. Excuse if this sounds negative, but this is my experience so far.
I have a repository and a service that speaks to the repository. Controllers call service.
My data layer is NOT persistence independent, as the classes were generated by SQL metal. Because of this I have a lot of unnecessary functionality. Ideally I'd like to have POCO, but I didn't find a good way to achieve this yet.
*Update: Of course Microsoft hasn't messed anything up - I did. I didn't fully understand the tools that were at my disposal. The major flaw in what I have done, was that I have chosen a wrong technology for persisting my entities. LINQ to SQL works well in stateful applications as the data context can be easily tracked. However, this is not a case in stateless context. What would be the right choice? Entity Framework code first or code only work pretty well, but what's more importantly, is that it shouldn't matter. MVC, or front end applications must should not aware of how data is persisted. *
When creating entites I can use object binding:
[HttpPost]
public ActionResult Create(Customer c)
{
// Persistance logic and return view
}
This works great, MVC does some binding behind the scene and everything is "jolly good".
It wasn't "Jolly Good". Customer was a domain model, and what was worse, it was dependent on persistence medium, because it was generated by SQL metal. What I would do now, is design my domain model, which would be independent of data storage or presentation layers. I would then create view model from my domain model and use that instead.
As soon as I'd like to do some more complex, e.g. - save Order which is linked to the customer everything seems to break:
[HttpPost]
public ActionResult Create(Order o)
{
// Persistance logic and return view
}
To persist an order I need Customer or at least CustomerId. CustomerId was present in the view, but by the time it has got to Create method, it has lost CustomerId. I don't fancy sitting around debugging MVC code as I won't be able to change it in a hosting envrionment either way.
Ok, a bit of moaning here, sorry. What I would do now, is create a view model called NewOrder, or SaveOrder, or EditOrder depending on what I'm trying to achieve. This view model would contain all the properties that I'm interested in. Out-of-the-box auto binding, as the name implies, will bind submitted values and nothing will be lost. If I want custom behaviour, then I can implement my own "binding" and it will do the job.
Alternative is to use FormCollection:
[HttpPost]
public ActionResult Create(FormCollection collection)
{
// Here I use the "magic" UpdateModel method which sometimes works and sometimes doesn't, at least for LINQ Entities.
}
This is used in books and tutorials, but I don't see a point in a method which has an alternative: TryUpdateModel - if this crashes or model is invalid, it attempts to update it either way. How can you be certain that this is going to work?
Autobinding with view models will work the most of the time. If it doesn't, then you can override it. How do you know it will always work? You unit test it and you sleep well.
Another approach that I have tried is using ViewModel - wrapper objects with validation rules. This sounds like a good idea, except that I don't want to add annotations to Entity classes. This approach is great for displaying the data, but what do you do when it comes to writing data?
[HttpPost]
public ActionResult Create(CustomViewWrapper submittedObject)
{
// Here I'd have to manually iterate through fields in submittedObject, map it to my Entities, and then, eventually, submit it to the service/repository.
}
** View model is a good way forward. There would have to be some mapping code from view model to the domain model, which can then be passed to the relevant service. This is not a correct way, but it's one way of doing it. Auto mapping tools are you best friends and you should find the one that suits your requirements, otherwise you'll be writing tons of boilerplate code.**
Am I missing something out or is this the way Microsoft MVC3 should work? I don't see how this is simplifying things, especiialy in comparisson to Java MVC.
I'm sorry if this sounds negative, but this has been my experience so far. I appreciate the fact that the framework is constantly being improved, methods like UpdateModel get introduced, but where is the documentation? Maybe it's time to stop and think for a little bit? I prefer my code to be consistent throughout, but with what I have seen so far, I have no confidence whatsoever that this is a right way forward.
I love the framework. There is so much to learn and it's not a lot more exciting then it has ever been. Should probably make another post regarding web forms. I hope this is helpful.
1) For the case of saving an order, and not having CustomerId present. If Order has a CustomerId property on it, and you have a stongly typed view, then you can persist this back to your controller action by adding
#Html.HiddenFor(model => model.CustomerId)
Doing this will have the default model binder populate things for you.
2) With respect to using a view model, I would recommend that approach. If you utilize something like AutoMapper you can take some of the pain out of redundant mapping scenarios. If you use something like Fluent Validation then you can separate validation concerns nicely.
Here's a good link on a general ASP.NET MVC implementation approach.
I don't think your issue is with asp.net MVC but with all the pieces You Choose to use together.
You want it raw and simple?
Use POCOs all around, and implement the repository where you need it.
I haven't used Java MVC, but it'd make the whole question look less like a rant if you include how you solved the particular problem in there.
Let's clear some misconceptions or maybe miscommunication:
You can pass complex objects through a post to the view. But you only want to do so if it makes sense, see next bullet
The sample you picked there rings some alarms. Accepting Customer data or CustomerID for an order and not checking authorization can be a Big security hole. The same could be said for an Order depending on what you are accepting/allowing. This is a Huge case for the use of ViewModels, regardless of POCOs, LINQ, Asp.net MVC or Java MVC.
You can pass simple values not being showed through a post to the view. It's done with hidden fields (which asp.net MVC supports very simply to use the model value), and in some scenarios it generates the hidden fields for you.
You are in no way forced to use linq2sql with Asp.net MVC. If you find it lacking for how you intend to use it, move away from it. Note I love linq2sql, but how it is tied to your view of what you can do with asp.net mvc is weird.
" I worked on Java MVC projects and they were clean and simple". Working on a project is not the same as designing the project yourself. Design skills does affect what you get out of anything. Not saying is your case, but just wanted to point that out given the lack of specifics on what you're missing from Java MVC.
"My data layer is NOT persistence independent, as the classes were generated by SQL metal. Because of this I have a lot of unnecessary functionality. Ideally I'd like to have POCO, but I didn't find a good way to achieve this yet". You picked the wrong technology, linq2sql is Not meant to fit that requirement. It haven't been a problem in the projects I've used it, but everything is designed in such a way that way less tied to its specifics than you seem to be. That said, just move to something else. btw, You should have shared what you used with Java MVC.
"CustomerId was present in the view, but by the time it has got to Create method, it has lost CustomerId." If the property is in Order, You can bet your code has the bug. Now, that'd have been a totally different Real question, why it isn't using the CustomerId / such question would come with: your Customer class, the View, what you are passing to the View ... answers would include, but not be limited to: inspect the HTML source in the browser to see what value you are really posting with the source (alternatively use fiddler to see the same), make sure that CustomerId really has the value when you pass it to the View.
You said: ""magic" UpdateModel method which sometimes works and sometimes doesn't". It's not magic, you can see what it does and certainly find information on it. Something is off in the information you are posting, my bet is non optional fields or wrong values for information that's parsed ... views support adding validations for that. Without the validations, this can be lacking.
You said in a comment: "After UpdateModel is called, i can't explicitly set the CustomerId, I'll have to retrieve a customer object and then assign it to the order, which seems like an overhead as all that I need is CustomerId" ... you are accepting a CustomerId that is user input (even if it is a hidden field), you really want to Validate that input. Additionally you are contradicting yourself, you claim to just need CustomerId, but then you say you need the full Customer Object related to the order bound. Which is it, if you are only binding the CustomerId, you still need to go get that Customer and assign it to the property. There is no magic besides the scenes ...
Also in a comment: "Update model is something I'm avoiding completely now as I don't know how it will behave with LINQ entities. In the view model class I have created constructor that converts LINQ entity to my view model. This decreased amount of code in controller, but still doesn't feel right". Reason to use ViewModel (or EditModel) is not because it is linq2sql ... it is because, among many other reasons, you are exposing a model that allows to manipulate way beyond what you actually want to allow the user to modify. Exposing the raw model, if it has fields the user shouldn't be allowed to modify, is the real issue.
If your view is correctly defined then you can easily do this >
[HttpPost]
public ActionResult Create(Order o, int CustomerId)
{
//you got the id, life back to jolly good (hopefully)
// Persistance logic and return view
}
EDIT:
as attadieni mentioned, by correct view I meant you have something like this inside the form tag >
#Html.HiddenFor(model => model.CustomerId)
ASP.NET MVC will automatically bind to the respective parameters.
I must be missing the problem.
You have a controller Order with an Action of Create just like you said:
public class OrderController()
{
[HttpGet]
public ViewResult Create()
{
var vm = new OrderCreateViewModel {
Customers = _customersService.All(),
//An option, not the only solution; for simplicities sake
CustomerId = *some value which you might already know*;
//If you know it set it, if you don't use another scheme.
}
return View(vm);
}
[HttpPost]
public ActionResult Create(OrderCreateViewModel model)
{
// Persistance logic and return view
}
}
The Create action posts back a view model of type OrderCreateViewModel that looks like such.
public class OrderCreateViewModel
{
// a whole bunch of order properties....
public Cart OrderItems { get; set; }
public int CustomerId { get; set; }
// Different options
public List<Customer> Customers { get; set; } // An option
public string CustomerName { get; set; } // An option to use as a client side search
}
Your view has a dropdown list of customers which you could add as a property to the viewmodel or a textbox which you wire up to to searching on the server side via JQuery where you could set a hidden field of CustomerId when a match is made, however you decide to do it. And if you already know the customerId ahead of time (which some of the other posts seems to imply) then just set it in the viewmodel and bypass all the above.
You have all of your order data. You have the customer Id of the customer attached to this order. You're good to go.
"To persist an order I need Customer or at least CustomerId. CustomerId was present in the view, but by the time it has got to Create method, it has lost CustomerId."
What? Why? If CustomerId was in the view, set, and posted back, it's in the model for the HttpPost Create method which is exactly where you need it. What do you mean it's being lost?
The ViewModel gets mapped to a Model object of type order. As suggested, using AutoMapper is helpful...
[HttpPost]
public ActionResult Create(OrderCreateViewModel model)
{
if(!ModelState.IsValid)
{
return View(model);
}
// Persistance logic and return view
var orderToCreate = new Order();
//Build an AutoMapper map
Mapper.CreateMap<OrderCreateViewModel, Order>();
//Map View Model to object(s)
Mapper.Map(model, orderToCreate);
//Other specialized mapping and logic
_orderService.Create(orderToCreate);
//Handle outcome. return view, spit out error, etc.
}
It's not a necessity, you can map it manually, but it just makes things easier.
And you're set. If you don't want to use data annotations for validation, fine, do it in the service layer, use the fluent validation library mentioned, whatever you choose. Once you call the Create() method of your service layer with all the data, you're good to go.
Where's the disconnect? What are we missing?
ataddeini's answer is correct, I'm just trying to show a bit more code. Upvote ataddeini
If the Customer Id is already in the Order model (in this example) it should be available without extending the method signature. If you view the source on the rendered view, is the customer id correctly emitted in a hidden field within the form? Are you using the [Bind] attribute on the Order model class and inadvertently preventing the Customer Id from being populated?
I would think the Order table would include a CustomerID field, if so, the only problem is maybe you are not including any control in the view to keep that value, then is lost.
Try to follow this example.
1) GET action before sending to the View, let's say you assign the CustomerID at this point.
public ActionResult Create()
{
var o = new Order();
o.CustomerID = User.Identity.Name; // or any other wher you store the customerID
return View(o);
}
2) The View, if you don't use any control for the CustomerID, like textbox, combobox, etc, you must use a hidden field to keep the value.
#using (Html.BeginForm())
{
#Html.HiddenFor(m => m.CustomerID)
<label>Requested Date:</label>
#Html.TextBoxFor(m => m.DateRequested)
...
}
3) Finally, the POST action to get and persist the order. In here, as CustomerID was kept in the hidden value, the Model Binder will automatically put all the Form values into the Order object o, then you just need to use CRUD methods and persist it.
[HttpPost]
public ActionResult Create(Order o)
{
return View();
}
Can be two approaches for this, one to implicit save all Model values even if not used in the View, and the other is to keep only those values used. I think MVC is doing the right thing to follow the later, avoid unnecessary keep a lot of junk for bigger models, when the only think is, to name one, a CustomerName, somehow it can give you control on what data to keep through the whole cycle action-view-action and save memory.
For more complex scenarios, where not all fields are in the same model, you need to use ViewModels. For example for mater-detail scenarios you would create a OrderViewModel that has two properties: Order o, and IEnumerable< OrderDetail > od, but again, you will need explicit use the values in the View, or use hidden fields.
In recent releases now you can use POCO classes and Code-First that makes all cleaner and easier, You may want to try EF4 + CTP5.
if you are using services (aka; service layer, business facade), to process lets say the OrderModel, you can extract an Interface, and get your ViewModel/DTO to implement it, so that you can pass back the ViewModel/DTO to the service.
If you are using Repositories to directly manage the data (without a servie layer) in the controller, then you can do it the good old way of Loading the object from a repository and then doing an UpdateModel on it.
[HttpPost]
public ActionResult Create(string customerCode, int customerId, Order order)
{
var cust = _customerRepository.Get(customerId);
cust.AddOrder(order);//this should carry the customerId to the order.CustomerId
}
Also, URLs might help a bit where it makes sense, I mean you can add the customer identifier in the url to create the order for.
UpdateModel should work, if your FormCollection has values for non-nullable properties and they are empty/null in the FormCollection, then UpdateModel should fail.

Categories

Resources