I am trying to follow the typical pattern of overriding the ControllerContext in order to mock HttpContext. In my case I specifically want to test HTTP POSTS so i need to mock Request.Form.
I have tried all 3 flavours found on google - with Moq, with Rhino.Mocks and with the MVCContrib.TestHelpers. For my specifics I've not been able to find a solution.
When my controller tries to bind the model, i get the following error:
Object reference not set to an instance of an object.
System.NullReferenceException: Object reference not set to an instance of an object.
at Microsoft.Web.Infrastructure.DynamicValidationHelper.ValidationUtility.CollectionReplacer.GetUnvalidatedCollections(HttpContext context)
at System.Web.Helpers.Validation.Unvalidated(HttpRequest request)
at System.Web.Mvc.FormValueProviderFactory.<.ctor>b__0(ControllerContext cc)
at System.Web.Mvc.FormValueProviderFactory.GetValueProvider(ControllerContext controllerContext)
at System.Web.Mvc.ValueProviderFactoryCollection.<>c__DisplayClassc.<GetValueProvider>b__7(ValueProviderFactory factory)
at System.Linq.Enumerable.WhereSelectEnumerableIterator`2.MoveNext()
at System.Linq.Enumerable.WhereSelectEnumerableIterator`2.MoveNext()
at System.Collections.Generic.List`1..ctor(IEnumerable`1 collection)
at System.Linq.Enumerable.ToList(IEnumerable`1 source)
at System.Web.Mvc.ValueProviderFactoryCollection.GetValueProvider(ControllerContext controllerContext)
at System.Web.Mvc.Controller.TryUpdateModel(TModel model)
at eServices.Admin.Web.Controllers.User.UserController.Search() in UserController.cs: line 56
at eServices.Admin.Specs.Controllers.when_the_user_controller_is_posted_the_manage_users_find_form.<.ctor>b__1() in UserControllerSpecs.cs: line 96
Which appears to suggest it is not finding the mocked form. Here's the test code snippet:
MoqHttpContext MoqHttpCon开发者_高级运维text = new MoqHttpContext();
var sut = new UserController(
UserRepository,
EmailService,
SessionProvider);
var controllerContext = new ControllerContext
(new RequestContext(MoqHttpContext.GetHttpContext(), new RouteData()), sut);
sut.ControllerContext = controllerContext;
MoqHttpContext.FormData.Add("FindCriteria.SearchText", "searchText");
MoqHttpContext.FormData.Add("FindCriteria.AccountIsPending", "true");
sut.Search();
...
in the controller:
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Search()
{
var manageUsersViewModel = new ManageUsersViewModel();
TryUpdateModel(manageUsersViewModel);
...
Any ideas or better solutions for testing POSTs?
Using MvcContrib.TestHelper:
// arrange
var sut = new SomeController();
var tcb = new TestControllerBuilder();
tcb.InitializeController(sut);
var formValues = new FormCollection()
{
{ "FindCriteria.SearchText", "searchText" },
{ "FindCriteria.AccountIsPending", "true" },
};
sut.ValueProvider = formValues.ToValueProvider();
// act
var actual = sut.Search();
// assert
...
Any ideas or better solutions for testing POSTs?
Yes: instead of using TryUpdateModel
have your controller actions directly take the view model as argument:
[HttpPost]
public ActionResult Search(ManageUsersViewModel model)
{
...
}
then in your unit test:
// arrange
var sut = new SomeController();
var model = new ManageUsersViewModel
{
FindCriteria = new FindCriteria
{
SearchText = "searchText",
AccountIsPending = true
}
};
// act
var actual = sut.Search(model);
// assert
...
精彩评论