为ASP.NET MVC应用程序创建单元测试

这篇文章主要演示如何为ASP.NET MVC应用程序创建单元测试。这里我们讨论了如何建三类单元测试
测试控制器返回特定的视图
测试控制器返回特定的View Data
测试控帽器返回特定的Action Result

一、创建要测试的控制器
我们把要测试的控制器命名为ProductController,代码如下:
Listing 1 – ProductController.cs
using System;
using System.Web.Mvc;
namespace Store.Controllers
{     
public class ProductController : Controller     
{          
  public ActionResult Index()          
   {               
    // Add action logic here               
    throw new NotImplementedException();          
   }          
   public ActionResult Details(int Id)          
   {               
   return View("Details");          
   }     
}
}
ProductController控制器包含两个控制动作Index()和Details()。两个控制器都返回视图,另外Details()动作接收一个名子为Id的形参

二、测试控制器返回的视图。(原创:灰灰虫的家 http://hi.baidu.com/grayworm
假设我们要测试ProductController是否返回正确的视图,确保ProductController.Details()动被能够被调用,并返回Details视图。下面的代码包含了测试ProductController.Details()动作的单元测试
Listing 2 – ProductControllerTest.cs
using System.Web.Mvc;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Store.Controllers;
namespace StoreTests.Controllers
{     
[TestClass]     
public class ProductControllerTest     
{          
[TestMethod]          
   public void TestDetailsView()          
   {               
   var controller = new ProductController();               
    var result = controller.Details(2) as ViewResult;               
    Assert.AreEqual("Details", result.ViewName);
          
   }     
}
}
在上面的代码中我们定义了一个TestDetailsView()方法,这个方法包含了三行代码,第一行代码是创建ProductController类的实例。第二行代码调用控制器的Details()动作方法。最后一行代码检查Details()动作是否返回Details视图。
ViewResult.ViewName属性表示由控制层返回视图的名称。控制器有两种返回视图的方法:
1.控制器动作以显式地返回视图名称
public ActionResult Details(int Id) {      return View("Details"); }

2.控制器动作返回视图的默认名称是控制器动作名
public ActionResult Details(int Id) {      return View(); }

但如果我们要测试返回视图的名称,那我们必须在控制器动作中显式返回视图名称。

我们可以按Ctrl+R,A来运行单元测试,也可以点击Run All Tests in Solution按钮,如下图所示,如果单元测试运行通过,我们会在Test Results窗口中看到测试结果,如下图所示


《图1》


《图2》

三、测试控制器返回的ViewData(原创:灰灰虫的家 http://hi.baidu.com/grayworm
MVC控制器通过ViewData向视图层返回数据。例如,假设我们想在调用ProductController.Details()动作后,显示特定产品的详细信息。那我们可以在Details()动作中创建Product实例,并使用ViewData把它传递给Details视图
Details()动作代码如下所示,返回一条产品信息。
Listing 3 – ProductController.cs
using System;
using System.Web.Mvc;
namespace Store.Controllers
{     
public class ProductController : Controller     
{          
   public ActionResult Index()          
   {               
    // Add action logic here               
    throw new NotImplementedException();          
   }          
   public ActionResult Details(int Id)          
   {               
    var product = new Product(Id, "Laptop");               
   return View("Details", product);          
   }     
}
}

首先,在Details动作中我们创建一个Product的实例,该实例是一台laptop计算机,然后我们把它传入View()方法中。
我们可以编写单元测试,以检查在Details()返回的ViewData数据中是否包含预期的数据。下面的代码是来测试ProductController.Details()动作是否返回一个laptop计算机的产品实例。
Listing 4 – ProductControllerTest.cs
using System.Web.Mvc;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Store.Controllers;
namespace StoreTests.Controllers
{     
[TestClass]     
public class ProductControllerTest     
{          
[TestMethod]          
   public void TestDetailsViewData()          
   {               
    var controller = new ProductController();               
   var result = controller.Details(2) as ViewResult;               
   var product = (Product) result.ViewData.Model;               
   Assert.AreEqual("Laptop", product.Name);          
   }     
}
}
在上面的代码中,TestDetailsView()方法用来测试Details()控制动作返回的ViewData。该ViewData是Details()动作以ViewResult的属性形式出现的。ViewDataModel包含传递给视图的产品。该测试就是检查控制器动作返回的ViewData中产品的名子是否是LapTop。

四、测试控制器返回的Action Result(原创:灰灰虫的家 http://hi.baidu.com/grayworm
复杂的控制器动作往往根据传入的参数不同而返回不同类型的Action Result。控制器动作可以返回各种Action Result。如ViewResult、RedirectToRouteResult和JsonResult。
例如下面的代码,Details动作接收一个Id参数,当我们传递合法的Id(Id>=1)进去的时候,Details()动作返回Details视图,如果传递的Id不合法(Id<1)时,Details()动作跳转到Index()动作。
Listing 5 – ProductController.cs
using System;
using System.Web.Mvc;
namespace Store.Controllers
{     
public class ProductController : Controller     
{          
   public ActionResult Index()          
   {               
    // Add action logic here               
    throw new NotImplementedException();          
   }          
public ActionResult Details(int Id)          
   {               
   if (Id < 1)                    
    return RedirectToAction("Index");               
    var product = new Product(Id, "Laptop");               
    return View("Details", product);
          
   }     
}
}

我们可以编写下面的测试代码来测试Details()动作的行为。下面的测试代码主要来检验当我们传递-1给Details()动作时,能否跳转到Index视图。
Listing 6 – ProductControllerTest.cs
using System.Web.Mvc;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Store.Controllers;
namespace StoreTests.Controllers
{     
[TestClass]     
public class ProductControllerTest     
{          
  [TestMethod]          
   public void TestDetailsRedirect()          
   {               
    var controller = new ProductController();               
   var result = (RedirectToRouteResult) controller.Details(-1);               
    Assert.AreEqual("Index", result.Values["action"]);
          
   }     
}
}

当我们在控制器动作中调用RedirectToAction()方法时,控制器动作返回RedirectToRouteResult。测试代码检查RedirectToRouteResult是否要把用户重定向到Index动作上。

总结
在这篇文章中我们学习如何为MVC控制器动作创建单元测试。
首先,我们学习检验控制器动作是否返回正确的视图。使用ViewResult.ViewName属笥来验证返回的视图名称
然后,我们学习如何检验返回的ViewData的内容。这里我们检验控制器动作是否在ViewData中返回正确的产品信息。
最后,我们讨论了如何测试控制器是否返回多个ActionResult。这里我们测试了控制器返回的是ViewResult还是RedirectToRouteResult。

完整的中文版《单元测试之道C#版》。单元测试不但会使你的工作完成得更轻松,而且会令你的设计变得更好,甚至大大减少你花在调试上面的时间。 在我们上面的小故事里面,Pat 因为假设底层的代码是正确无误的而卷入麻烦之中,先是高层代码中使用了底层代码;然后这些高层代码又被更高层的代码所使用,如此往复。在对这些代码的行为没有任何信心的前提下,Pat 等于是在假设上面用竖立卡片堆砌了一间房子——只要将下面卡片轻轻移动,整间房子就会轰然倒塌。 当基本的底层代码不再可靠时,那么必需的改动就无法只局限在底层。虽然你可以修正底层的问题,但是这些对底层代码的修改必然会影响到高层代码,于是高层代码也连带地需要修改;以此递推,就很可能会动到更高层的代码。于是,一个对底层代码的修正,可能会导致对几乎所有代码的一连串改动,从而使修改越来越多,也越来越复杂。于是,整间由卡片堆成的房子就由此倒塌,从而使整个项目也以失败告终。 Pat 总是说:“这怎么可能呢?”或者“我实在想不明白为什么会这样”。如果你发现自己有时候也会有这种想法,那么这通常是你对自己的代码还缺乏足够信心的表现——你并不能确认哪些是工作正常的而哪些不是。 为了获得Dale 所具有的那种对代码的信心,你需要“询问”代码究竟做了什么,并检查所产生的结果是否确实和你所期望的一致。 这个简单的想法描述了单元测试的核心内涵:这个简单有效的技术就是为了令代码变得更加完美。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值