Disadvantages of the ASP.NET MVC Framework - The Second Disadvantage
(Page 2 of 4 )
(2) Unit Test Makes the Learning Curve Steeper
Now, from many authorities, ASP.NET MVC is boosted to be superior to ASP.NET Web Forms in terms of test-driven development. Certainly, the MVC model helps to achieve separation of controller and view issues (in the classic ASP.NET Web Forms, the two are in one .aspx page) for the main purpose of simplifying testing. However, it increases the learning curve for those without a TDD (i.e. Test-Driven Development) background.
On the whole, the main causes that increase the steepness of the learning curve with ASP.NET MVC include at least two aspects:
(a) Piles of new basic concepts, such as ViewData, TempData, RouteCollection, Controller Action, Linq to SQL, Lambda Expression, Custom Route, and HTML Helpers, all of which constitute the bindings that tie the View, Controller, and Model. It is not until you have to grasped these entirely new concepts that you can start to write applications using ASP.NET MVC. For reference to these new concepts, I suggest you to visit Stephen Walther’s blog articles at http://weblogs.asp.net/stephenwalther/default.aspx.
(b) Another important stumbling block for newbies to learning ASP.NET MVC is the TDD (i.e. Test-Driven Development). TDD is an evolutionary approach to development which combines test-first development, where you write a test before you write just enough production code to fulfill that test, and refactoring. Test-Driven Development is related to the test-first programming concepts of Extreme Programming, begun in the late 20th century.
In the modern software development field, TDD becomes one of the standard software developing patterns and is welcomed by developers from all over the world. In fact, in the case of ASP.NET Web Forms, there is also TDD, but it’s more difficult to perform compared with the ASP.NET MVC solution.
Therefore, to accept ASP.NET MVC means to accept TDD. And for this, you’d better have some experience with the .NET platform-based Mock frameworks (such as Moq , Rhino Mocks , Typemock Isolator ) and Unit Test frameworks (such as Visual Studio Unit Test, NUnit , xUnit.NET ).
To explore the puzzle with respect to MVC, I’ll show you some examples. First, let’s take a look at the simplest test method (herein "Index") that the system automatically generates:
public class HomeControllerTest{
[TestMethod]
public void Index()
{
// Setup
HomeController controller = new HomeController();
// Execute
ViewResult result = controller.Index() as ViewResult;
// Verify
ViewDataDictionary viewData = result.ViewData;
Assert.AreEqual("Home Page", viewData["Title"]);
Assert.AreEqual("Welcome to ASP.NET MVC!", viewData["Message"]);
}
In this simplest case, we can only use the built-in Visual Studio Unit Test tool to finish the test method. Because, as the controller action method Index() indicates, there are only the two simple ViewData related strings to be transferred to the view "Index," with the release of ASP.NET MVC Preview 4 (it allows the controller action method to return an "ActionResult" as the executing result of the method, which permits postponing the execution of the result), there’s no need to make Mock objects in this simple case—only with the help of the static class Assert can the test task be performed.
Next, let’s examine a rather complex case (you can refer to it in the related downloadable source code with this article), under which we will resort to a third-party Mock framework, Moq, to accomplish the test. By the way, in the attached sample project named "MVCeProduct" I’ve successfully tested the Action methods: Category(int id), New(), Create(), Edit(int id), and Update(int id). For brevity, I will only introduce the unit test process for the first action method, namely "Category."
Okay, to gain a clearer understanding we’ll first look at the source code of the controller (in our case named "ProductsController") action method, as follows:
//…… (omitted)
using System.Web.Mvc;
using System.Linq;
using System.Web.Routing;
using MvcApplication.Models;
namespace MvcApplication.Controllers
{
public class ProductsController : Controller
{
NorthwindRepository repository;
public ProductsController()
: this(new NorthwindRepository(new eProductDataContext()))
{ }
public ProductsController(NorthwindRepository context)
{
this.repository = context;
}
public ActionResult Category(int id)
{
Category category = repository.Categories.SingleOrDefault(c => c.CategoryID == id);
ViewData["CategoryName"] = category.CategoryName;
return View("List", category);
}
First of all, we’ve defined a private property repository of the public class NorthwindRepository. To create a public class, NorthwindRepository, is to simplify the controller action methods programming, since only using the Model class (in this case "eProductDataContext") automatically generated by the Linq to SQL technique by the system is, in some cases, pretty fussy and even difficult to get through.
It’s just the complex private member that leads to trouble in unit-testing the action methods; this kind of case is very common in practical scenarios. For this, we can still find a solution—fall back upon Moq, the .NET platform-based Mock Object framework, to overcome the difficulty.
In the first line of code in the Category method, an instance of the Category class is created by retrieving the Model component (the lambda expressions are a MUST HAVE in similar cases like this). Since we will dispatch the task of judging whether the instance will be created successfully or not to the unit test, we do not make the judgment. Next, according to the inquired result we assign the related values to the Dictionary object named "ViewData." At last, the final line switches the current View to another View—List, and passes in the required Category instance.
Thus, in this case, the controller will render the contents of the view List using the passed ViewData data (less often) and the data from the Model component (more often).
Next: Category Method >>
More ASP.NET Articles
More By Xianzhong Zhu