九、为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。

posted @ 2012-04-21 15:35  大智若简  阅读(268)  评论(0编辑  收藏  举报