Has any one some example code of mocking request.form in a unit test, calling a controller and have the controller successfully bind the mock request.form to a view model using the Bind attribute or Controller.TryUpdateModel(model)?
This would appear to me to be a relatively common requirement, but alas a morning has gone and I am yet to find anything that's both adequate and working.
p.s. I've been chasing this all morning and have had no luck as the model binding is failing.
this always felt far harder than it needed to be. In the end the solution is simple and reasonably elegant although I'd prefer to see some conventional way to do this.
The trick is to add a FormCollection parameter to the Action:
this will be injected at run-time by MVC but allows mocking at test-time:
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Search([Bind(Prefix = "")] ManageUsersViewModel manageUsersViewModel, FormCollection form)
{
in the test:
var form = new FormCollection
{
{"FindCriteria.SearchText", "searchText"},
{"FindCriteria.AccountIsPending", "true"},
{"FindCriteria.TermsHaveBeenAccepted", "true"}
};
sut.Search(new ManageUsersViewModel(), form);
Edit
Also it appears you will need two other things - Bind - does not work, you will need to make sure your controller has a controllercontext AND you will need to explicity call UpdateModel:
controller.ControllerContext = new ControllerContext();
...
UpdateModel(manageUsersViewModel, form.ToValueProvider());
MVC 3 and I'm having to revert to this to test a simple form submission. INSANE IN THE MEMBRANE
Related
So I got Action Injection with Autofac implemented in large MVC solution, or so I thought.
Turned out there were cases were it did not work properly.
In one of the views we were posting back manually some extra information, and model binder was able to match it up properly, like here:
public ActionResult SomeAction(string[] textInfo, bool[] boolInfo, SomeViewModel viewModel)
However as soon as I enabled action injection:
builder.RegisterControllers(typeof(MvcApplication).Assembly).InjectActionInvoker();
The first two parameters, are being received as empty arrays.
My hypothesis would be, that autofac figures out, those were not provided, and provides default value for those. Is there any way to work around this behavior? With some parameter-level attribute perhaps?
I am using MVC3.
I have a pretty complex document class that contains many sub classes, lets call them "dog", "cat", "sheep", "cow", "goat".
I would like to use a generic controller to list, add and edit each of these classes.
Listing: I can list the relevant entity by passing in document model and iterating through the relevant class.
Edit: I need to get the id and identify the class of interest ie "dog" and then passing this as the model for editing. However next time round I may be editing a "cat" instance. Typically in MVC, as I understand it, one would ideally create a "DogController" and a "CatController". However since this document class is meant to be flexible I do not wish to hard code controllers. It may be necessary to add 2 more classes ie "horse" and "donkey".
At the moment I am using some if/else logic to ensure that the correct View is setup and called with the correct class. However this does feel as if I am breaking some MVC rules here.
Perhaps the answer lies in the use of a more formal ViewModel. Perhaps I can then have one of the properties as the subclass ("Dog"). Perhaps this can be swapped out at runtime using IOC?
Thoughts and pointers appreciated?
Thanks.
EDIT: I have tried to create 2 version of the "edit" action with different input models
[HttpPost]
public ActionResult Edit(Dog DogModel){}
[HttpPost]
public ActionResult Edit(Cat CatModel){}
I thought that due to method overloading MVC would be able to pick the correct one when the View posted, and then applied modelbinding.
Perhaps I am expecting too much or my understanding is lacking.
routes.MapRoute("Route", "{controller}/{action}/{name}/{id}", new { controller = "Animal", action = "search", name = "Home", id = 0 });
I have looked at previous questions but I could not find an answer to this particular scenario: I am using ASP.NET MVC 3 with a TDD approach. On my action methods, I use the return type of ViewResult opposed to ActionResult. This is fine to test my controllers. However, I need to test my view models as well as my controllers. I can do this quite happily by using the Model property on the ViewResultBase (which is implemented ViewResult but not ActionResult) for my model tests.
If there view model is null then I would like to redirect to another action. ActionResult supports a RedirectToAction method. However, ViewResult does not. I have tried the View method but the url does not change in the browser address bar. Also, I even tried the classic ASP.Net Response.Redirect(...), unsurprising, my unit test complains that the Response object has not been populated.
So, what approach do I use to achieve the equivalent of the ActionResult RedirectToAction for ViewResult?
if you need to return more than one result type from a controller, then set its return type to action result and then assert that the different conditions create the right result type.
For instance, in AJAX actions, you may want to return HttpStatusCodeResults to set the response property to errors should validation fail or there be an exception.
Then when the action has succeeded, you may want to return a partial view for display with the result.
Using this approach you can tailor your logic in your controller and still assert the different results are correct.
I found a way to do it. I used the following steps:
I set my action method return type to ActionResult
I changed its unit test to use ActionResult
I created two additional action methods with the return type of ResultView.
For each branch of the if statement of action method from [1], I redirected to the appropriate action method in [3], I used RedirectToAction to redirect.
I create two unit tests that use ResultView to test the action methods created in [3] and give me access to the underlying Model for each.
I do know there are several similar questions out there that address this topic, but I am not happy with the answers.
What I'd like is to take full advantage of the Razor View engine that can use strongly typed views with layout pages and partial views. I'd like to declare the model at the top of my views and just pass in that model. So simple! No dynamic models that need to be constructed explicitly just before passing them into the view. EDIT: I'd like to do this outside of a Web application, inside a class library that can construct email bodies. That's the real issue here is making use of the Razor View Engine that is not a web app.
The idea behind this is if I have a User object with 20 properties on it and my first version of the Welcome email only uses the Name, but later I want to edit the template to use their City, State or Email address, then I want to be able to update the Razor view without having to add code and recompile and redeploy.
I see RazorEngine and I love the idea and simplicity, but everything is a dynamic type. My issue here is that I'll be pulling data as models from the database. By having to shove things into a dynamic type, I don't get all my properties on the View.
I also see MvcMailer, which is also nice in theory but it suffers from the same issue which is that all data models passed into the view are dynamic and not strongly typed.
I've begun to build out my own version of this, which will require several web namespaces like System.Web.Mvc, System.Web.Razor and System.Web.WebPages - and I'm okay with that. The issue is the lack of HttpContext and ControllerContext and RouteData .... which I'm trying to mock/stub so the. I'm currently trying to investigate DisplayModes and figure out how to mock these outside a real Web context.
Is there a better way? If you're going to suggest one of the two previously mentioned frameworks, please note my issues and let me know if there are workarounds.
EDIT 2: After playing a bit more with the RazorEngine, the strongly typed models is not necessarily as much of an issue as I thought. What I'm left wishing for now is the ability to use a Layout page and partial views as well. I can hack around this with string placeholders that get replaced, but probably not very efficient AND not very flexible.
Assuming you are trying to achieve this from within an action method of a controller, here's a simple way to do it.
This method will give you the output of a strongly typed view :
public static string RenderViewToString(this Controller controller, string viewName, object model)
{
controller.ViewData.Model = model;
try
{
using (StringWriter sw = new StringWriter())
{
ViewEngineResult viewResult = ViewEngines.Engines.FindView(controller.ControllerContext, viewName, null);
ViewContext viewContext = new ViewContext(controller.ControllerContext, viewResult.View, controller.ViewData, controller.TempData, sw);
viewResult.View.Render(viewContext, sw);
viewResult.ViewEngine.ReleaseView(controller.ControllerContext, viewResult.View);
return sw.ToString();
}
}
catch(Exception ex)
{
return ex.ToString();
}
}
Then from within a controller method, all you need to do is call this method with the name of the view and the model.
public ActionResult Mail()
{
// whatever you use to get your User object
var model = new User();
var output = this.RenderViewToString("~/Views/User/Email.cshtml", model)
}
This will allow you to simulate the rendering of a strongly typed view, including its associated layout, and gather the output as a string which you can then use to send via email.
The answer is that it doesn't seem to matter whether the object passed in is dynamic or not. I can pass my strongly typed object, and it will be accepted as is without needing to make it dynamic or rebuild a dynamic object.
My prelim tests show that this should be fine and works well.
Try out Actionmailer.net: https://bitbucket.org/swaj/actionmailer.net/wiki/Home
I've been using it quite successfully.
Check out RazorMachine. It's like RazorEngine, but works with layouts.
I have been working on this MVC 3 Razor app and typically utilize view models for my views.
A fair number of my view models contain more information than just the particular entity that I am interacting with in my form. So my GET action handler will init the view model and provide each property with the intended value etc..
In my POST action handler I check to see if the model state is valid, if not I redisplay the form/view with errors.
In my POST action handler I find myself having to copy the code from my GET action handler in order to re-render the view again. How can I implement my controller actions so that I am not having to copy the code that is responsible for gathering the data for the view model?
I have tried allowing my action handler to handle both POST and GET but then I have the input params to deal with. My POST action handler will have the view model as an input parameter but for the GET action handler will not.
Your POST handler can return the ActionResult from the GET handler, as follows:
public ActionResult SomePageGet() {
var model = new SomePageViewModel();
// Populate ViewModel:
...
return View("SomePageGet", model);
}
[HttpPost]
public ActionResult SomePagePost(SomePageViewModel input) {
// Validate the model:
...
if (!ModelState.IsValid) {
// Return the GET page, with error messages:
return SomePageGet();
}
return View("Success");
}
Since the ModelState holds all error messages (and invalid input), the GET page will show them normally.
In situations like these we create builders for our view models.
Take a look at option 3 under this post.
You can simply refactor the common code into an extension method on the main entity you're working on.
Then call it as many times as you wish while staying DRY.
I don't precisely know what's the function of that common code, but mostly it will be related data for rich presentation.
In that case the solution which I prefer, is to let the view load the extra data from another action using RenderAction which can be later refactored to AJAX page update staying DRY and separating concerns of actions.
"...I find myself having to copy the code..."
I don't understand why; why can't you simply create a member in your controller and call it? Not everything in your controller has to be an Action. But you might want to look at builders instead as #ataddeini suggested.
Your POST action method should be able to just the the viewmodel type as the parameter, instead of all the individual pieces of data. If the viewmodel is more complex to build, you may want to write a modelbinder for your view model that can do that more complex work (your action method would still take the VM type as the parameter).
[HttpPost]
public ViewResult MyAction(MyViewModel model) {
// model should now be fully populated; check ModelState.IsValid though in case there are errors (such as the user entering "abc" for an int property)
}