views:

194

answers:

1

I am working through Steve Sanderson's book Pro ASP.NET MVC Framework and I having some issues with two unit tests which produce errors.

In the example below it tests the CheckOut ViewResult:

[AcceptVerbs(HttpVerbs.Post)]
public ViewResult CheckOut(Cart cart, FormCollection form)
{
        // Empty carts can't be checked out
        if (cart.Lines.Count == 0)
        {
            ModelState.AddModelError("Cart", "Sorry, your cart is empty!");
            return View();
        }

        // Invoke model binding manually
        if (TryUpdateModel(cart.ShippingDetails, form.ToValueProvider()))
        {
            orderSubmitter.SubmitOrder(cart);
            cart.Clear();
            return View("Completed");
        }
        else // Something was invalid
            return View();
}

with the following unit test

[Test]
public void Submitting_Empty_Shipping_Details_Displays_Default_View_With_Error()
{
    // Arrange
    CartController controller = new CartController(null, null);
    Cart cart = new Cart();
    cart.AddItem(new Product(), 1);
    // Act
    var result = controller.CheckOut(cart, new FormCollection {
        { "Name", "" }                                               
    });
    // Assert
    Assert.IsEmpty(result.ViewName);
    Assert.IsFalse(result.ViewData.ModelState.IsValid);
}

I have resolved any issues surrounding 'TryUpdateModel' by upgrading to ASP.NET MVC 2 (Release Candidate 2) and the website runs as expected.

The associated error messages are:

*Tests.CartControllerTests.Submitting_Empty_Shipping_Details_Displays_Default_View_With_Error: System.ArgumentNullException : Value cannot be null. Parameter name: controllerContext*

and the more detailed

at System.Web.Mvc.ModelValidator..ctor(ModelMetadata metadata, ControllerContext controllerContext) at System.Web.Mvc.DefaultModelBinder.OnModelUpdated(ControllerContext controllerContext, ModelBindingContext bindingContext) at System.Web.Mvc.DefaultModelBinder.BindComplexModel(ControllerContext controllerContext, ModelBindingContext bindingContext) at System.Web.Mvc.Controller.TryUpdateModel[TModel](TModel model, String prefix, String[] includeProperties, String[] excludeProperties, IValueProvider valueProvider) at System.Web.Mvc.Controller.TryUpdateModel[TModel](TModel model, IValueProvider valueProvider) at WebUI.Controllers.CartController.CheckOut(Cart cart, FormCollection form)

Has anyone run into a similar issue or indeed got the test to pass?

+1  A: 

You have to mock the HttpContext of the Controller to write such tests. The MvcContrib library makes it quite easy.

Another option that I prefer is to extract the logic done by "TryUpdateModel" into a custom ModelBinder. You can then test the logic of your checkout action method separately from the logic that update your model. No HttpContext needed.

Stephane
@serbrech In this case I am using Moq so I added the following and it works now. Thanks.var controllerContext = new Moq.Mock<ControllerContext>();controller.ControllerContext = controllerContext.Object;
Nicholas Murray
exactly, but you might have much more stuff to mock in the HttpContext later, and it gets quickly tricky to mock it properly.MvcContrib has some testHelpers that initialize all that for you ;).
Stephane