Proper signature for collection postback? - list

I haven an entity with a collection, something like: Product.Parts - when I render a view for Product, I call .EditorFor(x => x.Parts), which names my fields like: Parts[0].Name.
how do I declare my controller method that receives the postback so it can recompose those field names into something useful?
I originally tried:
[HttpPost] public ActionResult Create(Product entity)
which seemed to give me an object of the right type but was all empty (properties were null). then I tried:
[HttpPost] public ActionResult Create(List<Product> entity)
but in this case entity itself is null. also, this didn't help:
[HttpPost] public ActionResult Create(IList entity)
I guess I can always do this:
[HttpPost] public ActionResult Create(FormCollection form)
{
var Name = form["Product[0].Name"];
}
but that's eecky! help?

your 1st attempt should have worked... maybe have a look at the posted values in firefox or chrome?

Related

How does play framework unit testing controller methods

According to the documentation, to unit test controllers, I need to make my controllers a trait, then override the methods
http://www.playframework.com/documentation/2.2.0/ScalaTest
However, if I override my methods, i'm effectively not testing my logic. I may not be grasping something, but I don't see how this unit tests my controller's methods?
The problem with the example in the link you've provided is that it doesn't really show the benefit of having your controller implementation within a trait. In other words, the same example could've been accomplished without using traits by just testing the controller companion object directly.
The benefit of having your controller logic be within a trait is that it allows you to override dependencies that controller may have with mock implementations/values.
For example, you could define a controller as:
trait MyController extends Controller {
lazy val someService : SomeService = SomeServiceImpl
}
object MyController extends MyController
And in your test, you can override the service dependency:
val controller = new MyController {
override lazy val someService = mockService
}
As mentioned in the link the controllers in play are scala objects not classes so can't be instantiated like a class. By making it a trait instead you can make a test class which you can instantiate in your test. No need to override the methods though.
To use the example from the link, here we are making a TestController class that has the same behaviour as the ExampleController object. We don't need to override our index method as we inherit the behaviour from the trait.
Main file
trait ExampleController {
this: Controller =>
def index() = Action {
Ok("ok")
}
}
object ExampleController extends Controller with ExampleController
Test File
object ExampleControllerSpec extends PlaySpecification with Results {
class TestController() extends Controller with ExampleController
"Example Page#index" should {
"should be valid" in {
val controller = new TestController()
val result: Future[SimpleResult] = controller.index().apply(FakeRequest())
val bodyText: String = contentAsString(result)
bodyText must be equalTo "ok"
}
}
}
Here is my simple example of how you can check if some url is available
import org.specs2.mutable._
import org.specs2.runner._
import org.junit.runner._
import play.api.test._
import play.api.test.Helpers._
/**
* Set of tests which are just hitting urls and check
* if response code 200 returned
*/
#RunWith(classOf[JUnitRunner])
class ActionsSanityCheck extends Specification {
def checkIfUrlAccessible(url: String): Unit = {
val appRoute = route(FakeRequest(GET, url)).get
status(appRoute) must equalTo(OK)
contentType(appRoute) must beSome.which(_ == "text/html")
}
"Application" should {
"send 404 on a bad request" in new WithApplication {
route(FakeRequest(GET, "/nowhere")) must beNone
}
"render the index page" in new WithApplication {checkIfUrlAccessible("/")}
"render team page" in new WithApplication {checkIfUrlAccessible("/team")}
}
}

Unit testing HttpStatusCode in MVC4 'System.NullReferenceException'

I am trying to unit test HttpStatusCodes in MVC4 but I keep getting a 'System.NullReferenceException' when the controller tries to set the status code on Response, which makes sense as the action is getting called directly. I cant for the life of me work out how to do it without it becoming an integration test. Somebody must have done this, any ideas? See my existing code below.
Controller
public ActionResult Index()
{
Response.StatusCode = (int)HttpStatusCode.ServiceUnavailable;
Response.Headers.Add("Retry-After", "120");
return View();
}
Test
[Test]
public void IndexActionShouldReturn503StatusCode()
{
//Given
var controller = new HomeController();
//When
var result = controller.Index() as HttpStatusCodeResult;
//Then
result.StatusCode.Should().Be((int)HttpStatusCode.ServiceUnavailable);
}
Note
The requirement is for a friendly 'site down' page so I need to return both a view and the status code.
You're returning a ViewResult, then trying to cast it as a HttpStatusCodeResult in your unit test. Try returning a HttpStatusCodeResult instead of a view.
public ActionResult Index()
{
Response.Headers.Add("Retry-After", "120");
return new HttpStatusCodeResult(HttpStatusCode.ServiceUnavailable);
}

Calling an action of another controller - Design consideration for File Uploader - MVC 4

I have a situation where I'm debating how to architect my controllers.
Consider the following controller:
public class FileSharingController : Controller
{
private readonly ICommandBus commandBus;
public FileSharingController(ICommandBus commandBus)
{
this.commandBus = commandBus;
}
[HttpPost]
public ActionResult PrepareMetadata(int blocksCount, string fileName, long fileSize)
{
...
}
[HttpPost]
public ActionResult ClearFileMetadata(string fileName){
...
}
[HttpPost] [ValidateInput(false)] //$.ajax({ data: html5FormDataFileChunk , processData: false ... })
public ActionResult UploadBlock(string fileName, int blockId){
var fileUploadCommand = (FileUploadCommand)ExtractFromSessionData(fileName);
var result = commandBus.Submit(fileUploadCommand);
...
}
public ActionResult CommitFileUploads(string[] filesToCommit){
var commitFileUploadCommand = (CommitFileUploadCommand)ExtractFromSessionData(fileName);
var result = commandBus.Submit(commitFileUploadCommand );
...
}
In this controller, I use the command pattern and pass a model to my commandBus which interfaces with my domain. The first three [HttpPost] methods on the controller are for handling jQuery ajax calls from a responsive file uploading UI.
Consider the situation where a user fills out a form (an interview) and uploads some files along with it. Although the user can upload the files before submitting the form, I don't want the uploaded files to be committed until AFTER they submit the form and it passes validation. That is why the last method on the controller is not an http endpoint. As such I have the following controller:
public class InterviewController : Controller
{
[HttpGet]
public ActionResult UserInterview()
{
InterviewViewModel viewModel = new InterviewViewModel ();
return PartialView(viewModel);
}
[HttpPost] [AllowAnonymous]
public ActionResult UserInterview(InterviewViewModel viewModel)
{
if(ModelState.IsValid)
{
var fileSharingController = new FileSharingController();
fileSharingController.CommitFileUploads(viewModel.Files);
}
return PartialView(viewModel);
}
}
The problem is I'm using IoC to inject a commandBus into the FileSharingController so I cannot just instantiate it with default constructor as I am doing.
My options to consider:
Create a custom controller factory to allow instantiating my controller anywhere in the code.
Turn my FileSharingController in a WebAPI controller and treat as a service
Which is the better design path for this situation? If the latter case, how can I keep the CommitFileUploads() method private? I don't want it to be exposed as an endpoint that can be triggered without first validating the rest of the form.
You can instantiate your controller like this:
ICommandBus commandBus = DependencyResolver.Current.GetService<ICommandBus>();
var fileShareController = new FileSharingController(commandBus);
Generic GetService() method is extension method, so make sure that you have "using System.Web.Mvc;" line in the cs file.
But then, it's better to have helper class that is responsible for keeping/storing already uploaded files, and call it from both controllers, instead instantiating controllers manually.
For example:
public class FileUploadManager
{
public FileUploadManager(ICommandBus commandBus, HttpSessionStateBase sessionState)
{
//....
}
}
and then you call it:
ICommandBus commandBus = DependencyResolver.Current.GetService<ICommandBus>();
var fileShareController = new FileUploadManager(commandBus, this.HttpContext.Session);
Or, if you don't want to use DependencyResolver, you pass ICommandBus to both controller's constructors, and use that reference to instantiate helper class.
simply just create the object of another conroller and use all its public methods.

MVC 3 - Unit Test Controller Result

I am writing unit tests to test MVC 3 controllers. I want to ensure that that the view that comes back from the controller is the right view. In my unit test I have:
[Test]
public void It_Should_Return_The_Right_Page()
{
FormController fc = this.CreateFormController();
var view = fc.FindX();
Assert.AreEqual("FindX", view.ViewName);
}
In my controller, I have:
public ViewResult FindX()
{
return View();
}
This fails because ViewName is null. If I change the call to say return View("FindX") and explicitly define the view to be returned, it works. However, I would like to avoid this if possible. Is there a generally accepted way to approach this?
It sounds like what you want to convey is: Assert that the default view for this method was returned. One way to convey this is using this line:
var view = fc.FindX();
Assert.IsNull(view.ViewName)
But this doesn't convey your intent very well. One way to convey it more clearly is to create an extension method on ActionResult or ViewResult called AssertIsDefaultView like so:
public static class ActionResultAssertions
{
public static void AssertIsDefaultView(this ActionResult actionResult)
{
var viewResult = actionResult as ViewResult;
Assert.IsNotNull(viewResult);
Assert.IsNull(viewResult.ViewName);
}
}
Then in your test you can say:
var view = fc.FindX();
view.AssertIsDefaultView();
MvcContrib has a set of these assertions (I think the name of the method is AssertViewRendered), but I prefer to just write the extensions myself so I can understand MVC better.
If you don't set a viewname, then isn't ViewName being null the correct and expected outcome, so code your test accordingly.
Assert.IsNull(view.ViewName);
that worked for me
public ViewResult FindX()
{
return View("FindX");
}

Is there a way to unit test ASP.NET MVC ViewBag properties set in the view?

Say I have a view with the following code at the top of the page:
#{
ViewBag.Title = "About Us";
Layout = "~/Views/Shared/_Layout.cshtml";
}
And I have a controller method:
public ActionResult About()
{
return View();
}
How can I test that the ViewBag was set properly?
I have tried the following code.
[TestCase]
public void About()
{
var controller = new AboutController();
var ar = controller.About() as ViewResult;
Assert.AreEqual("About Us", ar.ViewBag.Title);
}
But I get the following result when I run the test:
Tests.Controllers.AboutControllerTests.About():
Expected: "About Us"
But was: null
Since both the ViewData and ViewBag use the same storage pattern, you should be able to use ViewData[yourKey] in your tests.
So your test will look like this:
[TestCase]
public void About()
{
var controller = new AboutController();
var ar = controller.About() as ViewResult;
Assert.AreEqual("About Us", ar.ViewData["Title"]);
}
Have you tried
Assert.AreEqual("About Us", controller.ViewBag.Title);
It works for me
The ViewResult returned by a controller has only a reference to the view that should be shown. The view is not even resolved at this time. The code there is never executed by your test.
What you should do is set ViewBag properties in the controller, not the view. Usually, the view will only read such values.
hope it helps
No, you cannot test views like this. The closest you might get is to render the view into a stream writer and then test the generated HTML. It is not something that is commonly done in unit tests. I would recommend you performing web tests in order to verify that the views are correct. You could create web tests with Visual Studio or there's also the free Selenium framework.
For what its worth, I found that the following worked fine:
Assert.AreEqual(_layout, result.ViewBag.Layout);
With result being the ViewResult
[TestMethod]
public async Task GetData() {
CtrlNameController controller = new CtrlNameController();
controller.ViewData["UserId"] = 1;
ViewResult result = await controller.GetData() as ViewResult;
Assert.IsNotNull(result);
}
ViewData["UserId"] is equal to View.UserId