I am learning to use unit testing and Moq for ASP.NET MVC 5. I am trying to write my first unit test for the index action of one of my controllers.
Here is the code for the index action.
[Authorize]
public class ExpenseController : Controller
{
private ApplicationDbContext db = new ApplicationDbContext();
// GET: /Expense/
public ActionResult Index()
{
return View(db.Expenses.ToList().Where(m => m.ApplicationUserId == User.Identity.GetUserId()));
}
}
All I want to do is just check that the returned view is not null
Something like this
[TestMethod]
public void ExpenseIndex()
{
// Arrange
ExpenseController controller = new ExpenseController();
// Act
ViewResult result = controller.Index() as ViewResult;
// Assert
Assert.IsNotNull(result);
}
Of course, this is not working because of the connecting to the database and the using of the ApplicationUserId so would you guys help me to moq and unit test this action or recommend me a tutorial where I can get familiar with mocking in ASP.NET MVC.
One way to do this is to abstract encapsulate the dependency in a virtual method, for example: Create a virtual method that returns the user expenses, now your controller should look like:
public class ExpenseController : Controller
{
private ApplicationDbContext db = new ApplicationDbContext();
// GET: /Expense/
public ActionResult Index()
{
return View(GetUserExpenses());
}
protected virtual List<Expense> GetUserExpenses()
{
return db.Expenses.ToList().Where(m => m.ApplicationUserId == User.Identity.GetUserId());
}
}
Then, create a stub class, that is derived from your controller, and override the GetUserExpenses() method. It should look like:
public class ExpenseControllerStub : ExpenseController
{
protected override List<Expense> GetUserExpenses()
{
return new List<Expense>();
}
}
Now in your unit test, create the instance from ExpenseControllerStub not from ExpenseController, and it should work:
[TestMethod]
public void ExpenseIndex()
{
// Arrange
ExpenseControllerStub controller = new ExpenseControllerStub();
// Act
ViewResult result = controller.Index() as ViewResult;
// Assert
Assert.IsNotNull(result);
}
This is how to do it manually. If you need to use a mocking framework for this, you will need to make GetUserExpenses() public not protected, then make a setup to return an empty expenses list, something like:
var mock = new Moq.Mock<ExpenseController>();
mock.Setup(m => m.GetUserExpenses().Returns(new List<Expense>());
But I don't prefer to make this method public! may be there is a way for Moq to configure/setup protected methods, but I am not aware of it.
Edit: A more better solution is to totally abstract the Expenses repository, in this case mocking it will be straight forward.
Another solution is to inject the DbContext to the controller constructor, and use a mocking framework to mock it and the Expenses DbSet. You can find a sample for doing this here
Edit#2: You may also use TestStack.FluentMVCTesting or MvcContrib.TestHelper to make you MVC testing easier.
It appears that something has changed with the release version of MVC4 that is causing the ExecuteResult method in a custom actionresult to not be invoked when the action result is tested from a unit test.
Here is a very contrived example that works in MVC3 and earlier versions of MVC4. Execute result is never "executed" when ran from a unit test. What am i missing here? Anyone else see this behavior?
Action result
public class SomeActionResult : ActionResult
{
public override void ExecuteResult(ControllerContext context)
{
if (context == null)
{
throw new ArgumentNullException("null context");
}
var view = new ViewResult {ViewName = "index"};
view.ExecuteResult(context);
}
}
Controller Action
[HttpPost]
public ActionResult Index(string something)
{
return new SomeActionResult();
}
Unit Test (Using MVCContrib)
[Test]
public void ShouldWork_but_doesnt_in_mvc4()
{
var controller = new HomeController();
var result = controller.Index("test");
result.AssertViewRendered();
}
Here is a very contrived example that works in MVC3 and earlier versions of MVC4.
You must have mistaken something. This won't work in MVC 3, 2, 1 either. And it is expected. Because a unit test means that you are unit testing something in isolation. So you have one unit test for the controller action and another to test your custom action result.
It is not the Index action that is invoking the ExecuteResult result method on the action result. This happens higher in the MVC execution pipeline during the execution of a user request. In your unit test you are simply calling the Index method.
So in order to unit test this controller action you simply assert that it returns an action result of the proper type:
[Test]
public void Ensure_That_Index_Action_Return_SomeActionResult()
{
// arrange
var controller = new HomeController();
// act
var result = controller.Index("test");
// assert
result.AssertResultIs<SomeActionResult>();
}
It is in another unit test of the SomeActionResult that you will manually invoke the ExecuteResult method and assert that this custom action result used a ViewResult.
Also it would seem more appropriate to have your custom action result derive from ViewResult rather than manually instantiating a ViewResult inside the ExecuteResult method and setting the ViewName:
public class SomeActionResult : ViewResult
{
public override void ExecuteResult(ControllerContext context)
{
this.ViewName = "Index";
base.ExecuteResult(context);
}
}
There is a simple controller that a querystring is read in constructor of it.
public class ProductController : Controller
{
parivate string productName;
public ProductController()
{
productName = Request.QueryString["productname"];
}
public ActionResult Index()
{
ViewData["Message"] = productName;
return View();
}
}
Also I have a function in unit test that create an instance of this Controller and I fill the querystring by a Mock object like below.
[TestClass]
public class ProductControllerTest
{
[TestMethod]
public void test()
{
// Arrange
var querystring = new System.Collections.Specialized.NameValueCollection { { "productname", "sampleproduct"} };
var mock = new Mock<ControllerContext>();
mock.SetupGet(p => p.HttpContext.Request.QueryString).Returns(querystring);
var controller = new ProductController();
controller.ControllerContext = mock.Object;
// Act
var result = controller.Index() as ViewResult;
// Assert
Assert.AreEqual("Index", result.ViewName);
}
}
Unfortunately Request.QueryString["productname"] is null in constructor of ProductController when I run test unit.
Is ther any way to fill a querystrin by a mocking and get it in constructor of a control?
There is a simple controller that a querystring is read in constructor of it.
You shouldn't be doing this and such controller shouldn't exist. The controller context is not yet initialized in the constructor and it will fail not only for the unit test but in real.
You should use the Initialize method where you have access to the request context.
I want to test a controller method in MVC unit test.
For my controller method to test, I require a Request.Files[] collection with length one.
I want to mock Request.Files[] as I have used a file upload control on my view rendered by controller method.
Can anyone please suggest how can I mock request.file collection in my unit test.
thanks,
kapil
You didn't mention what mocking framework you are using but here's how you would do it with Rhino Mocks:
Controller:
public class HomeController : Controller
{
public ActionResult Index()
{
return View(Request.Files.Count);
}
}
Unit test:
[TestMethod]
public void SomeTest()
{
// arrange
var controller = new HomeController();
var context = MockRepository.GenerateStub<HttpContextBase>();
var request = MockRepository.GenerateStub<HttpRequestBase>();
var files = MockRepository.GenerateStub<HttpFileCollectionBase>();
context.Stub(x => x.Request).Return(request);
files.Stub(x => x.Count).Return(5);
request.Stub(x => x.Files).Return(files);
controller.ControllerContext = new ControllerContext(context, new RouteData(), controller);
// act
var actual = controller.Index();
// assert
Assert.IsInstanceOfType(actual, typeof(ViewResult));
var viewResult = actual as ViewResult;
Assert.IsInstanceOfType(viewResult.ViewData.Model, typeof(int));
Assert.AreEqual(5, viewResult.ViewData.Model);
}
Remark: Using MVCContrib.TestHelper this test could be greatly simplified especially the context mocking part and the asserts as well:
[TestMethod]
public void SomeTest()
{
// arrange
var sut = new HomeController();
InitializeController(sut);
Files["test.txt"] = MockRepository.GenerateStub<HttpPostedFileBase>();
// act
var actual = sut.Index();
// assert
actual
.AssertViewRendered()
.WithViewData<int>()
.ShouldBe(1);
}
Scott Hanselman has a blog post covering this using Moq.
How can I test that my controller action is putting the correct errors in the ModelState when validating an entity, when I'm using DataAnnotation validation in MVC 2 Preview 1?
Some code to illustrate. First, the action:
[HttpPost]
public ActionResult Index(BlogPost b)
{
if(ModelState.IsValid)
{
_blogService.Insert(b);
return(View("Success", b));
}
return View(b);
}
And here's a failing unit test that I think should be passing but isn't (using MbUnit & Moq):
[Test]
public void When_processing_invalid_post_HomeControllerModelState_should_have_at_least_one_error()
{
// arrange
var mockRepository = new Mock<IBlogPostSVC>();
var homeController = new HomeController(mockRepository.Object);
// act
var p = new BlogPost { Title = "test" }; // date and content should be required
homeController.Index(p);
// assert
Assert.IsTrue(!homeController.ModelState.IsValid);
}
I guess in addition to this question, should I be testing validation, and should I be testing it in this way?
Hate to necro a old post, but I thought I'd add my own thoughts (since I just had this problem and ran across this post while seeking the answer).
Don't test validation in your controller tests. Either you trust MVC's validation or write your own (i.e. don't test other's code, test your code)
If you do want to test validation is doing what you expect, test it in your model tests (I do this for a couple of my more complex regex validations).
What you really want to test here is that your controller does what you expect it to do when validation fails. That's your code, and your expectations. Testing it is easy once you realize that's all you want to test:
[test]
public void TestInvalidPostBehavior()
{
// arrange
var mockRepository = new Mock<IBlogPostSVC>();
var homeController = new HomeController(mockRepository.Object);
var p = new BlogPost();
homeController.ViewData.ModelState.AddModelError("Key", "ErrorMessage"); // Values of these two strings don't matter.
// What I'm doing is setting up the situation: my controller is receiving an invalid model.
// act
var result = (ViewResult) homeController.Index(p);
// assert
result.ForView("Index")
Assert.That(result.ViewData.Model, Is.EqualTo(p));
}
I had been having the same problem, and after reading Pauls answer and comment, I looked for a way of manually validating the view model.
I found this tutorial which explains how to manually validate a ViewModel that uses DataAnnotations. They Key code snippet is towards the end of the post.
I amended the code slightly - in the tutorial the 4th parameter of the TryValidateObject is omitted (validateAllProperties). In order to get all the annotations to Validate, this should be set to true.
Additionaly I refactored the code into a generic method, to make testing of ViewModel validation simple:
public static void ValidateViewModel<TViewModel, TController>(this TController controller, TViewModel viewModelToValidate)
where TController : ApiController
{
var validationContext = new ValidationContext(viewModelToValidate, null, null);
var validationResults = new List<ValidationResult>();
Validator.TryValidateObject(viewModelToValidate, validationContext, validationResults, true);
foreach (var validationResult in validationResults)
{
controller.ModelState.AddModelError(validationResult.MemberNames.FirstOrDefault() ?? string.Empty, validationResult.ErrorMessage);
}
}
So far this has worked really well for us.
When you call the homeController.Index method in your test, you aren't using any of the MVC framework that fires off the validation so ModelState.IsValid will always be true. In our code we call a helper Validate method directly in the controller rather than using ambient validation. I haven't had much experience with the DataAnnotations (We use NHibernate.Validators) maybe someone else can offer guidance how to call Validate from within your controller.
I'm using ModelBinders in my test cases to be able to update model.IsValid value.
var form = new FormCollection();
form.Add("Name", "0123456789012345678901234567890123456789");
var model = MvcModelBinder.BindModel<AddItemModel>(controller, form);
ViewResult result = (ViewResult)controller.Add(model);
With my MvcModelBinder.BindModel method as follows (basically the same code used
internally in the MVC framework):
public static TModel BindModel<TModel>(Controller controller, IValueProvider valueProvider) where TModel : class
{
IModelBinder binder = ModelBinders.Binders.GetBinder(typeof(TModel));
ModelBindingContext bindingContext = new ModelBindingContext()
{
FallbackToEmptyPrefix = true,
ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(null, typeof(TModel)),
ModelName = "NotUsedButNotNull",
ModelState = controller.ModelState,
PropertyFilter = (name => { return true; }),
ValueProvider = valueProvider
};
return (TModel)binder.BindModel(controller.ControllerContext, bindingContext);
}
I was researching this today and I found this blog post by Roberto Hernández (MVP) that seems to provide the best solution to fire the validators for a controller action during unit testing. This will put the correct errors in the ModelState when validating an entity.
This doesn't exactly answer your question, because it abandons DataAnnotations, but I'll add it because it might help other people write tests for their Controllers:
You have the option of not using the validation provided by System.ComponentModel.DataAnnotations but still using the ViewData.ModelState object, by using its AddModelError method and some other validation mechanism. E.g:
public ActionResult Create(CompetitionEntry competitionEntry)
{
if (competitionEntry.Email == null)
ViewData.ModelState.AddModelError("CompetitionEntry.Email", "Please enter your e-mail");
if (ModelState.IsValid)
{
// insert code to save data here...
// ...
return Redirect("/");
}
else
{
// return with errors
var viewModel = new CompetitionEntryViewModel();
// insert code to populate viewmodel here ...
// ...
return View(viewModel);
}
}
This still lets you take advantage of the Html.ValidationMessageFor() stuff that MVC generates, without using the DataAnnotations. You have to make sure the key you use with AddModelError matches what the view is expecting for validation messages.
The controller then becomes testable because the validation is happening explicitly, rather than being done automagically by the MVC framework.
I agree that ARM has the best answer: test the behaviour of your controller, not the built-in validation.
However, you can also unit test that your Model/ViewModel has the correct validation attributes defined. Let's say your ViewModel looks like this:
public class PersonViewModel
{
[Required]
public string FirstName { get; set; }
}
This unit test will test for the existence of the [Required] attribute:
[TestMethod]
public void FirstName_should_be_required()
{
var propertyInfo = typeof(PersonViewModel).GetProperty("FirstName");
var attribute = propertyInfo.GetCustomAttributes(typeof(RequiredAttribute), false)
.FirstOrDefault();
Assert.IsNotNull(attribute);
}
In contrast to ARM, I don't have a problem with grave digging. So here is my suggestion. It builds on the answer of Giles Smith and works for ASP.NET MVC4 (I know the question is about MVC 2, but Google doesn't discriminate when looking for answers and I cannot test on MVC2.)
Instead of putting the validation code in a generic static method, I put it in a test controller. The controller has everything needed for validation. So, the test controller looks like this:
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Wbe.Mvc;
protected class TestController : Controller
{
public void TestValidateModel(object Model)
{
ValidationContext validationContext = new ValidationContext(Model, null, null);
List<ValidationResult> validationResults = new List<ValidationResult>();
Validator.TryValidateObject(Model, validationContext, validationResults, true);
foreach (ValidationResult validationResult in validationResults)
{
this.ModelState.AddModelError(String.Join(", ", validationResult.MemberNames), validationResult.ErrorMessage);
}
}
}
Of course the class does not need to be a protected innerclass, that is the way I use it now but I probably am going to reuse that class. If somewhere there is a model MyModel that is decorated with nice data annotation attributes, then the test looks something like this:
[TestMethod()]
public void ValidationTest()
{
MyModel item = new MyModel();
item.Description = "This is a unit test";
item.LocationId = 1;
TestController testController = new TestController();
testController.TestValidateModel(item);
Assert.IsTrue(testController.ModelState.IsValid, "A valid model is recognized.");
}
The advantage of this setup is that I can reuse the test controller for tests of all my models and may be able to extend it to mock a bit more about the controller or use the protected methods that a controller has.
Hope it helps.
If you care about validation but you don't care about how it is implemented, if you only care about validation of your action method at the highest level of abstraction, no matter whether it is implemented as using DataAnnotations, ModelBinders or even ActionFilterAttributes, then you could use Xania.AspNet.Simulator nuget package as follows:
install-package Xania.AspNet.Simulator
--
var action = new BlogController()
.Action(c => c.Index(new BlogPost()), "POST");
var modelState = action.ValidateRequest();
modelState.IsValid.Should().BeFalse();
Based on #giles-smith 's answer and comments, for Web API:
public static void ValidateViewModel<TViewModel, TController>(this TController controller, TViewModel viewModelToValidate)
where TController : ApiController
{
var validationContext = new ValidationContext(viewModelToValidate, null, null);
var validationResults = new List<ValidationResult>();
Validator.TryValidateObject(viewModelToValidate, validationContext, validationResults, true);
foreach (var validationResult in validationResults)
{
controller.ModelState.AddModelError(validationResult.MemberNames.FirstOrDefault() ?? string.Empty, validationResult.ErrorMessage);
}
}
See on answer edit above...
#giles-smith's answer is my preferred approach but the implementation can be simplified:
public static void ValidateViewModel(this Controller controller, object viewModelToValidate)
{
var validationContext = new ValidationContext(viewModelToValidate, null, null);
var validationResults = new List<ValidationResult>();
Validator.TryValidateObject(viewModelToValidate, validationContext, validationResults, true);
foreach (var validationResult in validationResults)
{
controller.ModelState.AddModelError(validationResult.MemberNames.FirstOrDefault() ?? string.Empty, validationResult.ErrorMessage);
}
}
Instead of passing in a BlogPost you can also declare the actions parameter as FormCollection. Then you can create the BlogPost yourself and call UpdateModel(model, formCollection.ToValueProvider());.
This will trigger the validation for any field in the FormCollection.
[HttpPost]
public ActionResult Index(FormCollection form)
{
var b = new BlogPost();
TryUpdateModel(model, form.ToValueProvider());
if (ModelState.IsValid)
{
_blogService.Insert(b);
return (View("Success", b));
}
return View(b);
}
Just make sure your test adds a null value for every field in the views form that you want to leave empty.
I found that doing it this way, at the expense of a few extra lines of code, makes my unit tests resemble the way the code gets called at runtime more closely making them more valuable. Also you can test what happens when someone enters "abc" in a control bound to an int property.