为 ASP.NET MVC 应用程序创建单元测试 (C#)
了解如何为控制器操作创建单元测试。 在本教程中,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
视图。 但是,视图的名称是从操作名称推断出来的。 如果要测试视图名称,则必须从控制器操作中显式返回视图名称。
可以通过输入键盘组合 Ctrl-R、A 或单击“ 在解决方案中运行所有测试 ”按钮来运行清单 2 中的单元测试 (请参阅图 1) 。 如果测试通过,你将在图 2 中看到“测试结果”窗口。
图 01:在解决方案中运行所有测试 (单击以查看全尺寸图像)
图 02:成功! (单击以查看全尺寸图像)
测试控制器返回的视图数据
MVC 控制器使用名为 View Data
的内容将数据传递到视图。 例如,假设你想要在调用 ProductController Details()
操作时显示特定产品的详细信息。 在这种情况下,可以创建在模型中定义的类的实例Product
() ,并通过利用 View Data
将该实例传递给Details
视图。
清单 3 中修改的 ProductController
包括返回 Product 的更新 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()
视图数据。 ViewData
通过调用 Details()
方法,在返回的 上ViewResult
公开为 属性。 属性 ViewData.Model
包含传递给视图的产品。 该测试只是验证“查看数据”中包含的产品是否名为 Laptop。
测试控制器返回的操作结果
更复杂的控制器操作可能会返回不同类型的操作结果,具体取决于传递给控制器操作的参数的值。 控制器操作可以返回各种类型的操作结果, ViewResult
包括 、 RedirectToRouteResult
或 JsonResult
。
例如,当您将有效的产品 ID 传递给操作Details
时,清单 5 中修改的操作Details()
将返回视图。 如果传递的产品 ID(值小于 1 的 ID)无效,则会重定向到操作 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 的 ID 传递给 方法时,是否已重定向 Index
到 Details()
视图。
清单 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
。