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