Inside controller I have view which returns simple object to the view.
public ActionResult SomeAction(int?id)
{
MyModel model = new MyModel();
return View(model);
}
how can I unit test this controller in order to check ViewResult,
basically to
check if view is initialized? Basically how can I mock this MyModel
inside my unit test?
[Test]
public void Can_Open_SomeAction()
{
// controller is already set inside `SetUp` unit step.
ViewResult res = this.controller.SomeAction() as ViewResult;
Assert.IsNotNull(res);
}
Update:
public ActionResult SomeAction(int?id)
{
MyModel model = new MyModel();
this.PopulatePageCombos(id);
return View(model);
}
The way you set this up, I presume you simply want to see if the model is not null:
[Test]
public void Can_Open_SomeAction()
{
ViewResult res = this.controller.SomeAction() as ViewResult;
Assert.IsNotNull(res);
var model = result.Model as MyModel;
Assert.IsNotNull(model);
}
Mocking would only make sense in a context where you'd get that model from an underlying interface, for example if you had:
public ActionResult SomeAction(int?id)
{
MyModel model = _myModelQuerier.Fetch(id.Value);
return View(model);
}
then you could get around something like
var modelQuerierMock = MockRepository.GenerateMock<IMyModelQuerier>();
modelQuerierMock.Stub(x => x.Fetch(Arg<int>.Is.Anything)).Return(new MyModel(2, "product"));
inside your test class
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 can't find any reason for this unit test to fail, but it does every time.
HomeController.cs:
[HandleError]
public class HomeController : Controller
{
public ActionResult Index()
{
var model = "Whatever";
return View( model );
}
}
HomeControllerTest.cs:
[TestClass]
public class HomeControllerTest
{
[TestMethod]
public void Index()
{
var controller = new HomeController();
var result = controller.Index() as ViewResult;
var model = result.ViewData.Model;
Assert.IsInstanceOfType( model, typeof(string) );
}
}
result is not null, nor is result.ViewData. But result.ViewData.Model is always null.
What in the world is causing this to fail? It's such a simple test...
EDIT
This is even weirder. If I create a class, and use the class as the model, it doesn't fail. It only fails when the model is a string! i.e., this passes:
HomeController.cs:
[HandleError]
public class HomeController : Controller
{
public ActionResult Index()
{
var model = new SomeClass
{
Name = "Whatever"
};
return View( model );
}
}
public class SomeClass
{
public string Name { get; set; }
}
HomeControllerTest.cs
[TestClass]
public class HomeControllerTest
{
[TestMethod]
public void Index()
{
var controller = new HomeController();
var result = controller.Index() as ViewResult;
var model = result.ViewData.Model;
Assert.IsInstanceOfType( model, typeof(SomeClass) );
}
}
The model is null that's why.
When you pass a string as the argument to View(string), the string is actually the view name, not a model.
Cast it as an object to use the overload for the model.
return View((object)model);
View(Object) - Creates a ViewResult object by using the model that renders a view to the response.
View(String) - Creates a ViewResult object by using the view name that renders a view.
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.
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.