指引网

当前位置: 主页 > 编程开发 > .NET >

ASP.NET MVC Tip #12 – 仿制控制器上下文

来源:网络 作者:佚名 点击: 时间:2017-11-17 03:41
[摘要] MVC ASP.NET MVC用程序比ASP.NET Web Forms应用程序更加可测试。ASP.NET MVC的每个特性从设计伊始就一直注意可测试性。然而,ASP.NET MVC应用那个程序中还是有一些方面是难以测试的。尤其你会发现,在
MVC

        ASP.NET MVC用程序比ASP.NET Web Forms应用程序更加可测试。ASP.NET MVC的每个特性从设计伊始就一直注意可测试性。然而,ASP.NET MVC应用那个程序中还是有一些方面是难以测试的。尤其你会发现,在ASP.NET MVC中测试ASP.NET内部仍然是一个挑战。

        我所说的“ASP.NET内部”是什么意思呢?就是指那些出现在HttpContext中的东西。也就是这些对象:

        Request.Forms——POST到一个页面的表单参数。

        Request.QueryString——传递到一个页面的查询字符串参数。

        User——发起页面请求的当前用户。

        Reqest.Cookies——传递到页面的浏览器Cookie。

        Session——会话状态对象。

        例如,假设你想对一个特定的控制器——其实是一个特定的会话状态条目——进行测试,你需要创建类似下面这样的单元测试:

         [TestMethod]
        public void TestSessionState()
        {
            // Arrange
            var controller = new HomeController();
            // Act
            var result = controller.TestSession() as ViewResult;
            // Assert
            Assert.AreEqual("wow!", controller.HttpContext.Session["item1"]);
        }

        该测试检查名为TestSession()的控制器操作是否向会话状态中添加了一个新的名叫item1的条目、其值是否为"wow!"。

        下面的控制器操作可以通过这一测试:

         public ViewResult TestSession()
        {
            Session["item1"] = "wow!";
            return View();
        }

        该控制操作向会话状态中插入了一个具有期望值的条目。

        不幸的是,如果你运行该单元测试,测试会失败。失败的原因是出现了一个NullReferenceException异常。此处的问题在于在单元测试的上下文中,会话状态并不存在。事实上,在一个测试方法中,任何ASP.NET内部的东西都不存在。这意味着你无法测试Cookies、表单参数、查询字符串参数和用户实体或用户角色。

        Mocking VS Stubbing

        如果你需要编写一个用户到了ASP.NET内部对象的单元测试,那么你必须做出选择。你有两种选择,一是使用Mock Ojbect Framework,或者是使用一组仿制类(Fake Class)。

        第一个选择是伪造(mock)ASP.NET内部对象,这可以使用Moq、Typemock Isolator或Rhino Mocks这样的Mock Ojbect Framework来完成。使用这些框架中的任何一种都可以生成假扮ASP.NET内部对象的对象。

        另外一种选择就是创建一组类,用于模拟ASP.NET内部对象。这组类可以只创建一次,然后在将来所有的ASP.NET MVC项目中使用它们。

        在这个Tip中,我将介绍第二种方法。我将向你展示如何通过创建一组标准的ASP.NET内部对象仿制类来简单地测试ASP.NET内部对象,而无需使用Mock Object Framework。

        创建仿制控制器上下文

        在该Tip的结尾,你可以下载到这些仿制类。我创建了一组ASP.NET内部对象仿制类,名字分别是:

         FakeControllerContext
        FakeHttpContext
        FakeHttpRequest
        FakeHttpSessionState
        FakeIdentity
        FakePrincipal

        在单元测试中创建FakeControllerContext的实例,并将其赋值给控制器的ControllerContext属性,就可以开始使用这些仿制类了。例如,下面展示了如何利用FakeControllerContext类来在单元测试中仿制一个特定的用户。

         var controller = new HomeController();
        controller.ControllerContext = new FakeControllerContext(controller, "Stephen");

        当把FakeControllerContext赋给控制器后,在单元测试的其余部分,控制器将会使用这个上下文。让我们来通过不同的例子看一看如何使用FakeControllerContext来模拟不同的ASP.NET内部对象。

        测试表单参数

        假设你希望向操作传递不同的表单参数来测试控制器操作的行为。另外,假设控制器操作象下面这样直接访问Reques.Form:

         public ActionResult Insert()
        {
          ViewData["firstname"] = Request.Form["firstName"];
          ViewData["lastName"] = Request.Form["lastName"];
          return View();
        }

        如何测试控制器操作呢?对于这种情况,你可以使用接受一组表单参数的FakeControllerContext构造器。下面的测试检查了firstName和lastName表单参数是否被保存到了视图数据中:

         [TestMethod]
        public void TestFakeFormParams()
        {
            // Create controller
            var controller = new HomeController();
            // Create fake controller context
            var formParams = new NameValueCollection { { "firstName", "Stephen" }, {"lastName", "Walther"} };
            controller.ControllerContext = new FakeControllerContext(controller, formParams);
            // Act
            var result = controller.Insert() as ViewResult;
            Assert.AreEqual("Stephen", result.ViewData["firstName"]);
            Assert.AreEqual("Walther", result.ViewData["lastName"]);
        }

        FakeControllerContext的表单参数是通过一个NameValueCollection创建的。表单参数的仿制集合被传递给FakeControllerContext的构造器。

        测试查询字符串参数

        假设你需要测试查询字符串参数是否被传递到一个视图中。查询字符串可以直接通过Request.QueryString集合访问。例如,控制器操作看起来可能是下面这样:

         public ViewResult Details()
        {
          ViewData["key1"] = Request.QueryString["key1"];
          ViewData["key2"] = Request.QueryString["key2"];
          ViewData["count"] = Request.QueryString.Count;
          return View();
        }

        在这种情况下,你可以通过将一个NameValueCollection传递给FakeControllerContext的构造器来仿制查询字符串:

         [TestMethod]
        public void TestFakeQueryStringParams()
        {
            // Create controller
            var controller = new HomeController();
            // Create fake controller context
            var queryStringParams = new NameValueCollection { { "key1", "a" }, { "key2", "b" } };
            controller.ControllerContext = new FakeControllerContext(controller, null, queryStringParams);
            // Act
            var result = controller.Details() as ViewResult;
            Assert.AreEqual("a", result.ViewData["key1"]);
            Assert.AreEqual("b", result.ViewData["key2"]);
            Assert.AreEqual(queryStringParams.Count, result.ViewData["count"]);
        }

        注意查询字符串要作为FakeControllerContext构造器的第二个参数传入。

 

------分隔线----------------------------