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");
}
Related
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
I have the following MVC Controller:
[RoutePrefix("our-brochure")]
[Route("{action=index}")]
public class OurBrochureController : Controller
{
public ActionResult Index()
{
return View();
}
}
Which works fine - if I go to http://localhost/our-brochure it goes to the index page and loads it as expected
However, for my navigation, I have a NavModel that works out the current controller and action using the following code:
var httpContext = new HttpContextWrapper(HttpContext.Current);
var routeData = System.Web.Routing.RouteTable.Routes.GetRouteData(httpContext);
var controllerName = routeData.Values["controller"].ToString();
var actionName = routeData.Values["action"].ToString();
This gives me the correct controller name of "OurBrochure" but routeData.Values["action"] is null.
Is this the expected behaviour as I would think that I would get "Index" as the action in the route data. If this is the expected behaviour, how do you get the current action name?
I have solved my problem, instead of getting the RouteData using the above methods, you can get it directly from the Current Context:
HttpContext.Current.Request.RequestContext.RouteData.Values["action"].ToString()
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);
}
}
Doing a simple test to verify the view name for a controllers action:
var controller = new UserController();
var result = controller.Login() as ViewResult;
Assert.AreEqual("Login", result.ViewName);
The result.ViewName is coming back with "", why would this be?
Are you specifying the view name in the controller Login method or are you leaving it at the default value (which is "")?
If you leaving it at the default value, which is common, you need to test for String.Empty instead of "Login".
if you have a view like this
public ActionResult Index()
{
return View();
}
then the ViewName property will take it's default value that is "" and if you specify the view name like this it'll work fine
public ActionResult Index()
{
return View("Index");
}
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.