Writing Unit Tests for an ASP.NET MVC Action Method that handles Ajax Request and Normal Request
In this blog post, I will demonstrate how to write unit tests for an ASP.NET MVC action method, which handles both Ajax request and normal HTTP Request. I will write a unit test for specifying the behavior of an Ajax request and will write another unit test for specifying the behavior of a normal HTTP request. Both Ajax request and normal request will be handled by a single action method. So the ASP.NET MVC action method will be execute HTTP Request object’s IsAjaxRequest method for identifying whether it is an Ajax request or not. So we have to create mock object for Request object and also have to make as a Ajax request from the unit test for verifying the behavior of an Ajax request. I have used NUnit and Moq for writing unit tests.
Let me write a unit test for a Ajax request
- [Test]
- public void Index_AjaxRequest_Returns_Partial_With_Expense_List()
- {
- // Arrange
- Mock<HttpRequestBase> request = new Mock<HttpRequestBase>();
- Mock<HttpResponseBase> response = new Mock<HttpResponseBase>();
- Mock<HttpContextBase> context = new Mock<HttpContextBase>();
- context.Setup(c => c.Request).Returns(request.Object);
- context.Setup(c => c.Response).Returns(response.Object);
- //Add XMLHttpRequest request header
- request.Setup(req => req["X-Requested-With"]).
- Returns("XMLHttpRequest");
- IEnumerable<Expense> fakeExpenses = GetMockExpenses();
- expenseRepository.Setup(x => x.GetMany(It.
- IsAny<Expression<Func<Expense, bool>>>())).
- Returns(fakeExpenses);
- ExpenseController controller = new ExpenseController(
- commandBus.Object, categoryRepository.Object,
- expenseRepository.Object);
- controller.ControllerContext = new ControllerContext(
- context.Object, new RouteData(), controller);
- // Act
- var result = controller.Index(null, null) as PartialViewResult;
- // Assert
- Assert.AreEqual("_ExpenseList", result.ViewName);
- Assert.IsNotNull(result, "View Result is null");
- Assert.IsInstanceOf(typeof(IEnumerable<Expense>),
- result.ViewData.Model, "Wrong View Model");
- var expenses = result.ViewData.Model as IEnumerable<Expense>;
- Assert.AreEqual(3, expenses.Count(),
- "Got wrong number of Categories");
- }
In the above unit test, we are calling Index action method of a controller named ExpenseController, which will returns a PartialView named _ExpenseList, if it is an Ajax request. We have created mock object for HTTPContextBase and setup XMLHttpRequest request header for Request object’s X-Requested-With for making it as a Ajax request. We have specified the ControllerContext property of the controller with mocked object HTTPContextBase.
- controller.ControllerContext = new ControllerContext(
- context.Object, new RouteData(), controller);
Let me write a unit test for a normal HTTP method
- [Test]
- public void Index_NormalRequest_Returns_Index_With_Expense_List()
- {
- // Arrange
- Mock<HttpRequestBase> request = new Mock<HttpRequestBase>();
- Mock<HttpResponseBase> response = new Mock<HttpResponseBase>();
- Mock<HttpContextBase> context = new Mock<HttpContextBase>();
- context.Setup(c => c.Request).Returns(request.Object);
- context.Setup(c => c.Response).Returns(response.Object);
- IEnumerable<Expense> fakeExpenses = GetMockExpenses();
- expenseRepository.Setup(x => x.GetMany(It.
- IsAny<Expression<Func<Expense, bool>>>())).
- Returns(fakeExpenses);
- ExpenseController controller = new ExpenseController(
- commandBus.Object, categoryRepository.Object,
- expenseRepository.Object);
- controller.ControllerContext = new ControllerContext(
- context.Object, new RouteData(), controller);
- // Act
- var result = controller.Index(null, null) as ViewResult;
- // Assert
- Assert.AreEqual("Index", result.ViewName);
- Assert.IsNotNull(result, "View Result is null");
- Assert.IsInstanceOf(typeof(IEnumerable<Expense>),
- result.ViewData.Model, "Wrong View Model");
- var expenses = result.ViewData.Model
- as IEnumerable<Expense>;
- Assert.AreEqual(3, expenses.Count(),
- "Got wrong number of Categories");
- }
In the above unit test, we are not specifying the XMLHttpRequest request header for Request object’s X-Requested-With, so that it will be normal HTTP Request. If this is a normal request, the action method will return a ViewResult with a view template named Index.
The below is the implementation of Index action method
- public ActionResult Index(DateTime? startDate, DateTime? endDate)
- {
- //If date is not passed, take current month's first and last date
- DateTime dtNow;
- dtNow = DateTime.Today;
- if (!startDate.HasValue)
- {
- startDate = new DateTime(dtNow.Year, dtNow.Month, 1);
- endDate = startDate.Value.AddMonths(1).AddDays(-1);
- }
- //take last date of start date's month, if end date is not passed
- if (startDate.HasValue && !endDate.HasValue)
- {
- endDate = (new DateTime(startDate.Value.Year,
- startDate.Value.Month, 1)).AddMonths(1).AddDays(-1);
- }
- var expenses = expenseRepository.GetMany(
- exp => exp.Date >= startDate && exp.Date <= endDate);
- //if request is Ajax will return partial view
- if (Request.IsAjaxRequest())
- {
- return PartialView("_ExpenseList", expenses);
- }
- //set start date and end date to ViewBag dictionary
- ViewBag.StartDate = startDate.Value.ToShortDateString();
- ViewBag.EndDate = endDate.Value.ToShortDateString();
- //if request is not ajax
- return View("Index",expenses);
- }
The index action method will returns a PartialView named _ExpenseList, if it is an Ajax request and will returns a View named Index if it is a normal request.
Source Code
The source code has been taken from my EFMVC app which can download from here