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

作者 :Stephen Walther

了解如何为控制器操作创建单元测试。 在本教程中,Stephen Walther 演示如何测试控制器操作是返回特定视图、返回一组特定的数据,还是返回不同类型的操作结果。

本教程的目的是演示如何为 ASP.NET MVC 应用程序中的控制器编写单元测试。 我们将讨论如何生成三种不同类型的单元测试。 你将了解如何测试控制器操作返回的视图、如何测试控制器操作返回的视图数据,以及如何测试一个控制器操作是否将你重定向到第二个控制器操作。

创建受测控制器

首先创建要测试的控制器。 名为 ProductController的控制器包含在清单 1 中。

列表 1 – ProductController.vb

Public Class ProductController
     Inherits System.Web.Mvc.Controller

     Function Index()
          ' Add action logic here
          Throw New NotImplementedException()
     End Function

     Function Details(ByVal Id As Integer)
          Return View("Details")
     End Function
End Class

包含 ProductController 两个名为 Index()Details()的操作方法。 这两种操作方法都返回视图。 请注意,操作 Details() 接受名为 Id 的参数。

测试控制器返回的视图

假设我们要测试 是否 ProductController 返回正确的视图。 我们希望确保在调用操作时 ProductController.Details() 返回“详细信息”视图。 清单 2 中的测试类包含一个单元测试,用于测试操作返回的 ProductController.Details() 视图。

清单 2 – ProductControllerTest.vb

Imports Microsoft.VisualStudio.TestTools.UnitTesting
Imports System.Web.Mvc
Imports Store

<TestClass()> Public Class ProductControllerTest
     <TestMethod()> Public Sub TestDetailsView()
          Dim controller As New ProductController()
          Dim result As ViewResult = controller.Details(2)
          Assert.AreEqual("Details", result.ViewName)

     End Sub
End Class

清单 2 中的 类包括名为 的测试 TestDetailsView()方法。 此方法包含三行代码。 第一行代码创建 类的新实例 ProductController 。 第二行代码调用控制器的操作 Details() 方法。 最后,最后一行代码检查操作返回的 Details() 视图是否为“详细信息”视图。

属性 ViewResult.ViewName 表示控制器返回的视图的名称。 有关测试此属性的一个大警告。 控制器可通过两种方式返回视图。 控制器可以显式返回如下所示的视图:

Function Details(ByVal Id As Integer)
     Return View("Details")
End Function

或者,可以从控制器操作的名称推断视图的名称,如下所示:

Function Details(ByVal Id As Integer)
     Return View()
End Function

此控制器操作还返回名为 的 Details视图。 但是,视图的名称是从操作名称推断出来的。 如果要测试视图名称,则必须从控制器操作中显式返回视图名称。

可以通过输入键盘组合 Ctrl-R、A 或单击“ 在解决方案中运行所有测试 ”按钮来运行清单 2 中的单元测试 (请参阅图 1) 。 如果测试通过,你将在图 2 中看到“测试结果”窗口。

在解决方案中运行所有测试

图 01:在解决方案中运行所有测试 (单击以查看全尺寸图像)

成功!

图 02:成功! (单击以查看全尺寸图像)

测试控制器返回的视图数据

MVC 控制器使用名为 View Data的内容将数据传递到视图。 例如,假设你想要在调用 ProductController Details() 操作时显示特定产品的详细信息。 在这种情况下,可以创建在模型中定义的类的实例Product () ,并通过利用 View Data将该实例传递给Details视图。

清单 3 中修改的 ProductController 包括返回 Product 的更新 Details() 操作。

清单 3 – ProductController.vb

Public Class ProductController
     Inherits System.Web.Mvc.Controller

     Function Index()
          ' Add action logic here
          Throw New NotImplementedException()
     End Function

     Function Details(ByVal Id As Integer)
          Dim product As New Product(Id, "Laptop")
          Return View("Details", product)
     End Function
End Class

首先,该 Details() 操作创建表示笔记本电脑的 Product 类的新实例。 接下来,类的 Product 实例作为第二个参数传递给 View() 方法。

可以编写单元测试来测试预期数据是否包含在视图数据中。 清单 4 中的单元测试测试在调用 ProductController Details() 操作方法时是否返回表示笔记本电脑的产品。

清单 4 – ProductControllerTest.vb

Imports Microsoft.VisualStudio.TestTools.UnitTesting
Imports System.Web.Mvc
Imports Store

<TestClass()> Public Class ProductControllerTest

     <TestMethod()> Public Sub TestDetailsViewData()
          Dim controller As New ProductController()
          Dim result As ViewResult = controller.Details(2)
          Dim product As Product = result.ViewData.Model
          Assert.AreEqual("Laptop", product.Name)
     End Sub
End Class

在清单 4 中 TestDetailsView() ,方法通过调用 方法测试返回的 Details() 视图数据。 ViewData通过调用 Details() 方法,在返回的 上ViewResult公开为 属性。 属性 ViewData.Model 包含传递给视图的产品。 该测试只是验证“查看数据”中包含的产品是否名为 Laptop。

测试控制器返回的操作结果

更复杂的控制器操作可能会返回不同类型的操作结果,具体取决于传递给控制器操作的参数的值。 控制器操作可以返回各种类型的操作结果, ViewResult包括 、 RedirectToRouteResultJsonResult

例如,当您将有效的产品 ID 传递给操作Details时,清单 5 中修改的操作Details()将返回视图。 如果传递的产品 ID(值小于 1 的 ID)无效,则会重定向到操作 Index()

列表 5 – ProductController.vb

Public Class ProductController
     Inherits System.Web.Mvc.Controller

     Function Index()
          ' Add action logic here
          Throw New NotImplementedException()
     End Function

     Function Details(ByVal Id As Integer)
          If Id < 1 Then
               Return RedirectToAction("Index")
          End If
          Dim product As New Product(Id, "Laptop")
          Return View("Details", product)
     End Function
End Class

可以使用清单 6 中的单元测试来测试操作的行为 Details() 。 清单 6 中的单元测试验证在将值为 -1 的 ID 传递给 方法时,是否已重定向 IndexDetails() 视图。

清单 6 – ProductControllerTest.vb

Imports Microsoft.VisualStudio.TestTools.UnitTesting
Imports System.Web.Mvc
Imports Store

<TestClass()> Public Class ProductControllerTest

     <TestMethod()> Public Sub TestDetailsRedirect()
          Dim controller As New ProductController()
          Dim result As RedirectToRouteResult = controller.Details(-1)
          Assert.AreEqual("Index", result.Values("action"))
     End Sub
End Class

在控制器操作中调用 RedirectToAction() 方法时,控制器操作将 RedirectToRouteResult返回 。 测试将检查 是否 RedirectToRouteResult 将用户重定向到名为 的 Index控制器操作。

总结

本教程介绍了如何为 MVC 控制器操作生成单元测试。 首先,你了解了如何验证控制器操作是否返回了正确的视图。 你已了解如何使用 ViewResult.ViewName 属性来验证视图的名称。

接下来,我们研究了如何测试 的内容 View Data。 你已了解如何检查调用控制器操作后是否返回了View Data正确的产品。

最后,我们讨论了如何测试是否从控制器操作返回不同类型的操作结果。 你已了解如何测试控制器是返回 ViewResult 还是 RedirectToRouteResult