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
  • 相关阅读:
    java中判断图片格式并且等比例压缩图片
    如何将freemarker文件转化为html文件
    细数用anaconda安装mayavi时出现的各种问题
    利用java代码生成keyStore
    时间戳获取 天/月/日等until
    navicat连接msql Client does not support authentication protocol requested by server; consider upgrading MySQL client
    类初始化和构造器初始化的区别
    git从自己账号切换到公司的账号,剪项目失败
    求吸血鬼数(1000~10000,thinking)
    vue。js的时间的格式化
  • 原文地址:https://www.cnblogs.com/Leo_wl/p/2568872.html
Copyright © 2011-2022 走看看