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

2009-12-24 来源:msdn.microsoft.com

 

本教程的目的是解释如何为 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。


火龙果软件/UML软件工程组织致力于提高您的软件工程实践能力,我们不断地吸取业界的宝贵经验,向您提供经过数百家企业验证的有效的工程技术实践经验,同时关注最新的理论进展,帮助您“领跑您所在行业的软件世界”。
资源网站: UML软件工程组织