共用方式為


建立 ASP.NET MVC 應用程式的單元測試 (C#)

作者:Stephen Walther

瞭解如何建立控制器動作的單元測試。 在本教學課程中,Stephen Walther 會示範如何測試控制器動作是否傳回特定視圖、傳回特定資料集,或傳回不同類型的動作結果。

本教學課程的目標是示範如何在 ASP.NET MVC 應用程式中撰寫控制器的單元測試。 我們討論如何建置三種不同類型的單元測試。 您將瞭解如何測試控制器動作傳回的檢視、如何測試控制器動作所傳回的檢視資料,以及如何測試一個控制器動作是否會將您重新導向至第二個控制器動作。

建立受測控制器

讓我們從建立我們想要測試的控制器開始。 名為 ProductController 的控制器包含在清單 1 中。

清單 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 的參數。

測試控制器傳回的檢視

假設我們想要測試 ProductController 是否傳回正確的檢視。 我們想要確定叫用 ProductController.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() 動作傳回的檢視是否為 [詳細資料] 檢視。

ViewResult.ViewName 屬性代表控制器所傳回之檢視的名稱。 測試這個屬性的一項大警告。 控制器可以傳回檢視的方式有兩種。 控制器可以明確地傳回如下的檢視:

public ActionResult Details(int Id)
{
     return View("Details");
}

或者,檢視的名稱可以從控制器動作的名稱推斷,如下所示:

public ActionResult Details(int Id)
{
     return View();
}

此控制器動作也會傳回名為 Details 的檢視。 不過,檢視的名稱是從動作名稱推斷而來。 如果您想要測試檢視名稱,則必須從控制器動作明確傳回檢視名稱。

您可以在清單 2 中執行單元測試,方法是輸入鍵盤組合 Ctrl-R、A 或按一下 [執行解決方案中的所有測試] 按鈕 (請參閱圖 1)。 如果測試通過,您會看到圖 2 中的 [測試結果] 視窗。

執行解決方案中的所有測試

圖 01:執行解決方案中的所有測試 (按一下以檢視完整大小的圖片)

成功!

圖 02:成功! (按一下以檢視完整大小的圖片)

測試控制器傳回的檢視資料

MVC 控制器會使用稱為 View Data 的內容,將資料傳遞至檢視。 例如,假設您想要在叫用 ProductController Details() 動作時顯示特定產品的詳細資料。 在此情況下,您可以建立 Product 類別的執行個體 (定義於模型中),並利用 View Data 將執行個體傳遞至 Details 檢視。

清單 3 中修改的 ProductController 包含會傳回產品的更新 Details() 動作。

清單 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 中的單元測試會測試當您呼叫 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() 方法所傳回的檢視資料。 在叫用 Details() 方法所傳回的 ViewResult 上,ViewData 會公開為屬性。 ViewData.Model 屬性包含傳遞至檢視的產品。 測試只會驗證檢視資料中包含的產品名稱為筆記型電腦。

測試控制器所傳回的動作結果

更複雜的控制器動作可能會根據傳遞給控制器動作的參數值,傳回不同類型的動作結果。 控制器動作可以傳回各種類型的動作結果,包括 ViewResultRedirectToRouteResultJsonResult

例如,當您將有效的產品標識碼傳遞至動作時,清單 5 中的修改 Details() 動作會傳回 Details 檢視。 如果您傳遞無效的產品識別碼 (值為小於 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 中的單元測試會驗證當值為 -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 中是否傳回正確的產品。

最後,我們討論了如何測試是否從控制器動作傳回不同類型的動作結果。 您已瞭解如何測試控制器是否傳回 ViewResultRedirectToRouteResult