本教程的目的是解釋如何為 ASP.NET MVC 應用程式中的控制器編寫單元測試。我們將討論如何建立三種不同類型的單元測試。您將瞭解如何測試控制器操作返回的視圖、如何測試控制器操作返回的視圖資料,以及如何測試一個控制器操作是否重新導向到另一個控制器操作。
建立測試控制器
我們首先建立要測試的控制器。程式清單 1 中包含名稱為 ProductController 的控制器。
程式清單 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"); } } } |
HomeController 包含兩個操作方法,名稱為 Index() 和 Details()。兩個操作方法都返回一個視圖。請注意, Details() 操作接受名稱為 Id 的參數。
測試控制器返回的視圖
假設要測試 ProductController 是否返回正確的視圖。希望確保當啟用 ProductController.Details() 操作時,返回 Details 視圖。程式清單 2 中的測試類別包含一個單元測試,用於測試由 ProductController.Details() 操作返回的視圖。
程式清單 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); } } } |
程式清單 2 中的類包含名稱為 TestDetailsView() 的測試方法。此方法包括三行代碼。第一行代碼建立一個 ProductController 類的新執行個體。第二行代碼啟用控制器的 Details() 操作方法。最後一行代碼檢查 Details() 操作返回的是否是 Details 視圖。
ViewResult.ViewName 屬性代表由控制器返回的視圖的名稱。測試該屬性時需要特別小心。控制器返回視圖有兩種方法。控制器可以顯式地返回視圖,如下所示:
public ActionResult Details(int Id) { return View("Details"); } |
另外,視圖的名稱可以引用控制器操作的名稱,如下所示:
public ActionResult Details(int Id) { return View(); } |
此控制器操作也返回名稱為 Details 的視圖。然而,視圖的名稱引用自操作的名稱。如果想要測試檢視名稱,則必須顯式地從控制器操作返回視圖名稱。
通過按 Ctrl-R,A 按鍵組合或單擊 Run All Tests in Solution 按鈕( 1 所示),可以運行程式清單 2 中的單元測試。如果通過測試,則將看到 2 所示的 Test Results 視窗。
圖 1:運行解決方案中的所有測試
圖 2:成功!
測試控制器返回的 View Data
MVC 控制器使用 View Data 將資料傳遞給視圖。例如,假設想要在啟用 ProductController Details() 操作時顯示某個產品的詳細資料。在這種情況下,可以建立 Product 類的執行個體(在模型中定義),然後利用 View Data 將執行個體傳遞給 Details 視圖。
程式清單 3 中修改後的 ProductController 包含更新的 Details() 操作,它返回 Product。
程式清單 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 類的新執行個體表示膝上型電腦。接下來,Product 類的執行個體被作為第二個參數傳遞給 View() 方法。
可以編寫測試單元測試預期的資料是否包含在視圖資料中。程式清單 4 中的單元測試用於測試表示膝上型電腦的 Product 是否在調用 ProductController Details() 操作方法時返回。
程式清單 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); } }} |
在程式清單 4 中,TestDetailsView() 方法通過啟用 Details() 方法測試返回的 View Data。ViewData 公開為 ViewResult(通過啟用 Details() 方法返回)上的一個屬性。ViewData.Model 屬性包含傳遞給視圖的產品。測試只是簡單地驗證封裝含在 View Data 中的產品名稱是 Laptop。
測試控制器返回的操作結果
較複雜的控制器操作可能返回不同類型的操作結果,具體取決於傳遞給控制器操作的參數值。控制器操作可以返回各種類型的操作結果,包括 ViewResult、RedirectToRouteResult 或 JsonResult。
例如,程式清單 5 中修改的 Details() 操作在將有效 Id 傳遞給操作時返回 Details 視圖。如果傳遞無效的產品 Id(Id 的值小於 1),則將重新導向到 Index() 操作。
程式清單 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); } }} |
可以使用程式清單 6 中的單元測試來測試 Details() 操作的行為。程式清單 6 中的單元測試驗證當 Id 值 -1 被傳遞到 Details() 方法時是否重新導向到 Index 視圖。
程式清單 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 屬性驗證視圖的名稱。
接下來,我們研究了如何測試 View Data 的內容。學習了如何檢查調用控制器操作後 View Data 中是否返回正確的產品。
最後,我們討論了如何測試控制器操作是否返回不同類型的操作結果。學習了如何測試控制器操作是返回 ViewResult 還是 RedirectToRouteResult。
轉自:http://www.uml.org.cn/Test/200912248.asp