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);
}
}
Related
I'm trying to write unit tests for a MVC application. im trying to test if my controller returns the correct view name.
This is the controller action im testing:
public IActionResult Index(string reportcode)
{
if(string.IsNullOrEmpty(reportcode))
ReportCode = reportcode;
ViewBag.GridSource = GetReportData(reportcode);
return View("Index");
}
This is my Unittest:
[Test]
public void Index_ReturnCorrectView()
{
var controller = new HomeController();
var result = controller.Index("COMD") as ViewResult;
Assert.AreEqual("Index", result.ViewName);
}
The error i get from the unit test is expected "Index" but got null.
I did a lot of search and most answers say the ViewName property should be set after you declare it when returning the view. I tried the same but it wont work still.
Thank you
The documentation for Controller.View() states:
This method overload of the View class returns a ViewResult object
that has an empty ViewName property. If you are writing unit tests for
controller actions, take into account the empty ViewName property for
unit tests that do not take a string view name.
At run time, if the ViewName property is empty, the current action
name is used in place of the ViewName property.
So when expecting a view with the same name as the current action we can just test that it's an empty string.
Alternatively, the Controller.View(ViewName, Model) method will set the ViewName.
My Controller Method
public ActionResult Index()
{
return View("Index");
}
Test Method
[TestMethod]
public void Index()
{
// Arrange
HomeController controller = new HomeController();
// Act
ViewResult result = controller.Index() as ViewResult;
// Assert
Assert.IsTrue(result.ViewName == "Index");
}
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.
I am using FluentValidation in my MVC project and have the following model and validator:
[Validator(typeof(CreateNoteModelValidator))]
public class CreateNoteModel {
public string NoteText { get; set; }
}
public class CreateNoteModelValidator : AbstractValidator<CreateNoteModel> {
public CreateNoteModelValidator() {
RuleFor(m => m.NoteText).NotEmpty();
}
}
I have a controller action to create the note:
public ActionResult Create(CreateNoteModel model) {
if( !ModelState.IsValid ) {
return PartialView("Test", model);
// save note here
return Json(new { success = true }));
}
I wrote a unit test to validate the behavior:
[Test]
public void Test_Create_With_Validation_Error() {
// Arrange
NotesController controller = new NotesController();
CreateNoteModel model = new CreateNoteModel();
// Act
ActionResult result = controller.Create(model);
// Assert
Assert.IsInstanceOfType(result, typeof(PartialViewResult));
}
My unit test is failing because it doesn't have any validation errors. This should succeed because model.NoteText is null and there is a validation rule for this.
It appears that FluentValidation isn't running when I run my controller test.
I tried adding the following to my test:
[TestInitialize]
public void TestInitialize() {
FluentValidation.Mvc.FluentValidationModelValidatorProvider.Configure();
}
I have this same line in my Global.asax to tie up the validators to the controllers automatically...but it doesn't appear to be working in my unit test.
How do I get this working correctly?
That's normal. Validation should be tested separately from controller actions, like this.
And to test your controller action simply simulate a ModelState error:
[Test]
public void Test_Create_With_Validation_Error() {
// Arrange
NotesController controller = new NotesController();
controller.ModelState.AddModelError("NoteText", "NoteText cannot be null");
CreateNoteModel model = new CreateNoteModel();
// Act
ActionResult result = controller.Create(model);
// Assert
Assert.IsInstanceOfType(result, typeof(PartialViewResult));
}
A controller shouldn't really know anything about fluent validation. What you need to test here is that if there is a validation error in the ModelState your controller action behaves correctly. How this error was added to the ModelState is a different concern that should be tested separately.
I'm using ASP.NET MVC 2, NUnit, Moq and MVC Contrib. I have written my first unit test ever, and I have a couple of questions regarding this test. My scenario is that I have an Index view. On this view I have a grid that displays all the news items.
Here is my INewsRepository class:
public interface INewsRepository
{
IEnumerable<News> FindAll();
}
My test class with the test method:
public class NewsControllerTest :TestControllerBuilder
{
private Mock<INewsRepository> mockNewsRepository;
private NewsController newsController;
[SetUp]
public void Init()
{
mockNewsRepository = new Mock<INewsRepository>();
newsController = new NewsController(mockNewsRepository.Object);
InitializeController(newsController);
}
[Test]
public void NewsController_Index()
{
// Arrange
var news = new Mock<IEnumerable<News>>();
mockNewsRepository.Setup(r => r.FindAll()).Returns(news.Object).Verifiable();
// Act
ActionResult actual = newsController.Index();
// Assert
mockNewsRepository.Verify();
actual
.AssertViewRendered()
.ForView("Index")
.WithViewData<News[]>()
.ShouldBe(news);
}
}
My view:
public ActionResult Index()
{
FakeNewsRepository fakeNewsRepository = new FakeNewsRepository();
IEnumerable<News> news = fakeNewsRepository.FindAll();
return View(news);
}
I need some pointers on the way that I did it. Am I in the correct direction? What should I add, what should I leave out? I want to do unit testing, am I mixing it with integration testing? Any extra advice would be appreciated.
When I run this test in the NUnit GUI console then I get an error back and I'mnot sure what it means:
MyProject.Web.Tests.Controllers.NewsControllerTest.NewsController_Index:
Moq.MockVerificationException : The following setups were not matched:
INewsRepository r => r.FindAll()
public ActionResult Index()
{
FakeNewsRepository fakeNewsRepository = new FakeNewsRepository();
IEnumerable<News> news = fakeNewsRepository.FindAll();
return View(news);
}
You cannot mock the repository that is hardcoded like this in your action. You are instantiating it inside the action, you will never be able to unit test this and mock the repository. The repository needs to be injected as a dependency. You could use an interface and pass this interface to the constructor of your controller:
public class HomeController: Controller
{
private readonly IRepository _repository;
public class HomeController(IRepository repository)
{
_repository = repository;
}
public ActionResult Index()
{
IEnumerable<News> news = _repository.FindAll();
return View(news);
}
}
Now in your unit test you could pass the mocked instance of your repository to the controller constructor and define expectations.
Also notice that MVCContrib.TestHelper is designed to work with Rhino Mocks. I am not quite sure whether it works fine with Moq.
i've got the following Action Method I'm trying to moq test. Notice the AcceptVerbs? I need to make sure i'm testing that.
here's the method.
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Create([Bind(Include = "Subject, Content")]Post post,
HttpPostedFileBase imageFileName)
{
...
}
here's the moq code i have...
[TestMethod]
public void Create_Action_snip_sniop_When_Http_Post_Is_Succesful()
{
// Arrange.
var mock = new Mock<ControllerContext>();
mock.SetupGet(m => m.HttpContext.Request.HttpMethod).Returns("POST");
// Snip some other arrangements.
var controller = PostController;
controller.ControllerContext = mock.Object;
// Act.
var viewResult = controller.Create(post, image.Object) as ViewResult;
// Assert.
Assert.IsNotNull(viewResult);
// TODO: Test that the request was an Http-Post.
what do i need to do to verify the request was a post?
Your attribute won't be invoked when running as a unit test because it is normally invoked by the ControllerActionInvoker as part of the Mvc "stack". What I've done in cases like this is to write a test to make sure that the correct attribute is applied to the action with the correct parameters. Then I trust that the framework will do its job correctly.
Doing this requires reflection:
public void Only_posts_are_allowed_to_my_action()
{
var method = typeof(MyController).GetMethod("MyAction");
var attribute = method.GetCustomAttributes(typeof(AcceptVerbsAttribute),false)
.Cast<AcceptVerbsAttribute>()
.SingleOrDefault();
Assert.IsNotNull( attribute );
Assert.AreEqual( 1, attributes.Count() );
Assert.IsTrue( attributes.Contains( HttpVerbs.Post ) );
}