zoukankan      html  css  js  c++  java
  • 模拟HttpContext 实现ASP.NET MVC 的单元测试

    众所周知 ASP.NET MVC 的一个显著优势即可以很方便的实现单元测试,但在我们测试过程中经常要用到HttpContext,而默认情况下单元测试框架是不提供HttpContext的模拟的,本文通过MOQ框架实现对HttpContext的模拟从而实现更便利的单元测试。 

    一、Moq框架使用

    Moq是一个非常优秀的模拟框架,可以实现对接口成员的模拟,常用在TDD中。 可在此处下载http://code.google.com/p/moq/downloads/list 也可以通过Nuget直接下载。

    先来看一个简单的moq应用

    1. 定义一个简单接口且不需要实现接口(Moq就是模拟框架因此不需要实现)

    View Code
    1 using System;
     2 using System.Collections.Generic;
     3 using System.Linq;
     4 using System.Text;
     5 
     6 namespace Stphen.Sample.ASPNETMvc.MockUnitTest.Bussiness
     7 {
     8    public interface IFoo
     9    {
    10        void DoSomeThing(string thingName);
    11        bool IsLoveFoo();
    12 
    13        string FooName { get; set; }
    14    }
    15 }
    复制代码
     1 using System;
     2 using System.Collections.Generic;
     3 using System.Linq;
     4 using System.Text;
     5 
     6 namespace Stphen.Sample.ASPNETMvc.MockUnitTest.Bussiness
     7 {
     8    public interface IFoo
     9    {
    10        void DoSomeThing(string thingName);
    11        bool IsLoveFoo();
    12 
    13        string FooName { get; set; }
    14    }
    15 }
    复制代码

    2. 在测试代码中我实现了对接口的Mock

    View Code
    using System;
    using System.Text;
    using System.Collections.Generic;
    using System.Linq;
    using Microsoft.VisualStudio.TestTools.UnitTesting;
    using Moq;
    using Stphen.Sample.ASPNETMvc.MockUnitTest.Bussiness;
    
    namespace Stephen.Sample.ASPNETMvc.MockUnitTest.TestProject
    {
        [TestClass]
        public class MockUnitTest
        {
            private readonly Mock<IFoo> _fooMock; 
    
            public MockUnitTest()
            {
                _fooMock = new Mock<IFoo>();
            }
    
            [TestMethod]
            public void MockDoSomeThingMethodTest()
            {
                _fooMock.Setup(foo => foo.DoSomeThing(It.IsAny<string>())).Callback((string s) => Console.WriteLine(s));
                _fooMock.Object.DoSomeThing("HelloWorld");
            }
    
            [TestMethod]
            public  void MockIsLoveMockFramewrokMethodTest()
            {
                _fooMock.Setup(foo => foo.IsLoveFoo()).Returns(true);
                Assert.AreEqual(true, _fooMock.Object.IsLoveFoo());
            }
    
            [TestMethod]
            public  void MockFooNamePropertyTest()
            {
                _fooMock.Setup(foo => foo.FooName).Returns("stephen");
                Assert.AreEqual("stephen",_fooMock.Object.FooName);
            }
        }
    }
    复制代码
    using System;
    using System.Text;
    using System.Collections.Generic;
    using System.Linq;
    using Microsoft.VisualStudio.TestTools.UnitTesting;
    using Moq;
    using Stphen.Sample.ASPNETMvc.MockUnitTest.Bussiness;
    
    namespace Stephen.Sample.ASPNETMvc.MockUnitTest.TestProject
    {
        [TestClass]
        public class MockUnitTest
        {
            private readonly Mock<IFoo> _fooMock; 
    
            public MockUnitTest()
            {
                _fooMock = new Mock<IFoo>();
            }
    
            [TestMethod]
            public void MockDoSomeThingMethodTest()
            {
                _fooMock.Setup(foo => foo.DoSomeThing(It.IsAny<string>())).Callback((string s) => Console.WriteLine(s));
                _fooMock.Object.DoSomeThing("HelloWorld");
            }
    
            [TestMethod]
            public  void MockIsLoveMockFramewrokMethodTest()
            {
                _fooMock.Setup(foo => foo.IsLoveFoo()).Returns(true);
                Assert.AreEqual(true, _fooMock.Object.IsLoveFoo());
            }
    
            [TestMethod]
            public  void MockFooNamePropertyTest()
            {
                _fooMock.Setup(foo => foo.FooName).Returns("stephen");
                Assert.AreEqual("stephen",_fooMock.Object.FooName);
            }
        }
    }
    复制代码

    第一个方法没有返回值,第二个有返回值分别用Mock 的 Callback 和 return 方法。

    二、通过Moq框架实现HttpContext的模拟(MvcContextMock)

    复制代码
      1 using System;
      2 using System.Collections.Generic;
      3 using System.Linq;
      4 using System.Text;
      5 using System.Web;
      6 using System.Web.Routing;
      7 using System.Web.Mvc;
      8 using Moq;
      9 namespace MvcContextMock
     10 {
     11     /// <summary>
     12     /// Mvc Context 工厂
     13     /// </summary>
     14     public static class MvcContextMockFactory
     15     {
     16         private static ControllerContext controllerContext = null;
     17         /// <summary>
     18         /// 创建ControllerContext
     19         /// </summary>
     20         /// <param name="controller">Controller</param>
     21         /// <returns></returns>
     22         public static ControllerContext CreateControllerContext(Controller controller)
     23         {
     24              controllerContext = new ControllerContext
     25                (
     26               CreateHttpContext(),
     27               new RouteData(),
     28                controller);
     29             return controllerContext;
     30         }
     31 
     32         /// <summary>
     33         /// 创建ControllerContext
     34         /// </summary>
     35         /// <param name="controller">Controller</param>
     36         /// <param name="contextBase">httpContextBase</param>
     37         /// <returns></returns>
     38         public static ControllerContext CreateControllerContext(Controller controller, HttpContextBase contextBase)
     39         {
     40              controllerContext = new ControllerContext
     41                (
     42               contextBase,
     43               new RouteData(),
     44                controller);
     45             return controllerContext;
     46         }
     47 
     48 
     49         /// <summary>
     50         /// 创建ControllerContext
     51         /// </summary>
     52         /// <param name="controller">controller</param>
     53         /// <param name="url">访问的url地址</param>
     54         /// <param name="httpMethod">访问的方法(get,post)</param>
     55         /// <param name="name">路由名称</param>
     56         /// <param name="pattern">路由格式</param>
     57         /// <param name="obj">路由默认值</param>
     58         /// <returns></returns>
     59         public static ControllerContext CreateControllerContext(Controller controller, string url, string httpMethod, string name, string pattern, string obj)
     60         {
     61              controllerContext = new ControllerContext
     62                 (
     63                 CreateHttpContext(),
     64                 GetRouteData(url, httpMethod, name, pattern, obj),
     65                 controller);
     66             return controllerContext;
     67         }
     68 
     69         /// <summary>
     70         /// 创建ControllerContext
     71         /// </summary>
     72         /// <param name="controller">controller</param>
     73         /// <param name="contextBase">HttpContextBase</param>
     74         /// <param name="url">访问的url地址</param>
     75         /// <param name="httpMethod">访问的方法(get,post)</param>
     76         /// <param name="name">路由名称</param>
     77         /// <param name="pattern">路由格式</param>
     78         /// <param name="obj">路由默认值</param>
     79         /// <returns></returns>
     80         public static ControllerContext CreateControllerContext(Controller controller, HttpContextBase contextBase, string url, string httpMethod, string name, string pattern, string obj)
     81         {
     82              controllerContext = new ControllerContext
     83                 (
     84                 contextBase,
     85                 GetRouteData(url, httpMethod, name, pattern, obj),
     86                 controller);
     87             return controllerContext;
     88         }
     89 
     90         /// <summary>
     91         /// 创建HttpContextBase
     92         /// </summary>
     93         /// <returns></returns>
     94         public static HttpContextBase CreateHttpContext()
     95         {
     96             var context = new Mock<HttpContextBase>();
     97             var request = new Mock<HttpRequestBase>();
     98             var response = new Mock<HttpResponseBase>();
     99             var session = new Mock<HttpSessionStateBase>();
    100             var server = new Mock<HttpServerUtilityBase>();
    101             response
    102                 .Setup(rsp => rsp.ApplyAppPathModifier(Moq.It.IsAny<string>()))
    103                 .Returns((string s) => s);
    104 
    105             context.Setup(ctx => ctx.Request).Returns(request.Object);
    106             context.Setup(ctx => ctx.Response).Returns(response.Object);
    107             context.Setup(ctx => ctx.Session).Returns(session.Object);
    108             context.Setup(ctx => ctx.Server).Returns(server.Object);
    109 
    110             return context.Object;
    111         }
    112 
    113         #region Private Method
    114         private static HttpContextBase CreateHttpContext(string url, string httpMethod)
    115         {
    116             var context = new Mock<HttpContextBase>();
    117             var request = new Mock<HttpRequestBase>();
    118             var response = new Mock<HttpResponseBase>();
    119             var session = new Mock<HttpSessionStateBase>();
    120             var server = new Mock<HttpServerUtilityBase>();
    121             response
    122                 .Setup(rsp => rsp.ApplyAppPathModifier(Moq.It.IsAny<string>()))
    123                 .Returns((string s) => s);
    124 
    125             request.Setup(req => req.AppRelativeCurrentExecutionFilePath).Returns(url);
    126             request.Setup(req => req.HttpMethod).Returns(httpMethod);
    127 
    128             context.Setup(ctx => ctx.Request).Returns(request.Object);
    129             context.Setup(ctx => ctx.Response).Returns(response.Object);
    130             context.Setup(ctx => ctx.Session).Returns(session.Object);
    131             context.Setup(ctx => ctx.Server).Returns(server.Object);
    132 
    133             return context.Object;
    134         }
    135 
    136         private static RouteData GetRouteData(string url, string httpMethod, string name, string pattern, string obj)
    137         {
    138             RouteTable.Routes.MapRoute(name, pattern, obj);
    139             var routeData =
    140                 RouteTable.Routes.
    141                 GetRouteData(CreateHttpContext(url, httpMethod));
    142             return routeData;
    143         }
    144         #endregion 
    145 
    146     }
    147 }
    复制代码


    三、在mvc中使用MockHttpContextFactory

     通过之前编写MvcContextMockFactory类我们在MVC单元测试中可以轻松的实现对HttpContext的模拟

    首先创建一个控制器

    View Code
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Web.Mvc;
    namespace Stephen.Sample.ASPNETMvc.MockUnitTest.Controller
    {
       public class HomeController:System.Web.Mvc.Controller
        {
          public ViewResult Index()
          {
              ViewData["controller"] = RouteData.Values["controller"];
              ViewData["action"] = RouteData.Values["action"];
              return View("Index");
          }
        }
    }
    复制代码
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Web.Mvc;
    namespace Stephen.Sample.ASPNETMvc.MockUnitTest.Controller
    {
       public class HomeController:System.Web.Mvc.Controller
        {
          public ViewResult Index()
          {
              ViewData["controller"] = RouteData.Values["controller"];
              ViewData["action"] = RouteData.Values["action"];
              return View("Index");
          }
        }
    }
    复制代码

    因为控制器中需要获取RouteData,RouteData来自于Controller下的ControllerContext

    单元测试中调用MockHttpContextFactory模拟ControllerContext,完成单元测试。

    View Code
    using System;
    using System.Text;
    using System.Collections.Generic;
    using System.Linq;
    using System.Web.Mvc;
    using Microsoft.VisualStudio.TestTools.UnitTesting;
    using MvcContextMock;
    using Stephen.Sample.ASPNETMvc.MockUnitTest.Controller;
    
    namespace Stephen.Sample.ASPNETMvc.MockUnitTest.TestProject
    {
        [TestClass]
        public class HomeControllerUnitTest
        {
            [TestMethod]
            public void IndexActionTest()
            {
                var homeController = new HomeController();
                homeController.ControllerContext = MvcContextMock.MvcContextMockFactory.CreateControllerContext(homeController, "~/Home/Index", "get", "DefaultRoute", "{controller}/{action}", null);
                ViewResult result= homeController.Index();
                Assert.AreEqual("Index",result.ViewName);
                Assert.AreEqual("Home",result.ViewData["controller"]);
                Assert.AreEqual("Index", result.ViewData["action"]);
            }
        }
    }
    复制代码
    using System;
    using System.Text;
    using System.Collections.Generic;
    using System.Linq;
    using System.Web.Mvc;
    using Microsoft.VisualStudio.TestTools.UnitTesting;
    using MvcContextMock;
    using Stephen.Sample.ASPNETMvc.MockUnitTest.Controller;
    
    namespace Stephen.Sample.ASPNETMvc.MockUnitTest.TestProject
    {
        [TestClass]
        public class HomeControllerUnitTest
        {
            [TestMethod]
            public void IndexActionTest()
            {
                var homeController = new HomeController();
                homeController.ControllerContext = MvcContextMock.MvcContextMockFactory.CreateControllerContext(homeController, "~/Home/Index", "get", "DefaultRoute", "{controller}/{action}", null);
                ViewResult result= homeController.Index();
                Assert.AreEqual("Index",result.ViewName);
                Assert.AreEqual("Home",result.ViewData["controller"]);
                Assert.AreEqual("Index", result.ViewData["action"]);
            }
        }
    }
    复制代码

    对于更简单的HttpContext在MockHttpContextFactory中有专用的方法生成与ControllerContext原理类似。

     
    分类: ASP.NET MVC
  • 相关阅读:
    June 26th 2017 Week 26th Monday
    June 25th 2017 Week 26th Sunday
    June 24th 2017 Week 25th Saturday
    June 23rd 2017 Week 25th Friday
    June 22nd 2017 Week 25th Thursday
    2018最佳网页设计:就是要你灵感爆棚!!!
    图片素材类Web原型制作分享-Pexels
    想要打动HR的心,UX设计师求职信究竟应该怎么写?
    【UXPA大赛企业专访】Mockplus:“设计替代开发”将成为现实
    2018年最好的医疗网站设计及配色赏析
  • 原文地址:https://www.cnblogs.com/Leo_wl/p/2568872.html
Copyright © 2011-2022 走看看