• [转]模拟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就是模拟框架因此不需要实现)

         1:  using System;
         2:   using System.Collections.Generic;
         3:   using System.Linq;
         4:   using System.Text;
         5:   
         6:   namespace Stphen.Sample.<a href="http://www.it165.net/pro/webasp/" target="_blank" class="keylink">ASP</a>NETMvc.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

       1:  using System;
       2:  using System.Text;
       3:  using System.Collections.Generic;
       4:  using System.Linq;
       5:  using Microsoft.VisualStudio.TestTools.UnitTesting;
       6:  using Moq;
       7:  using Stphen.Sample.<a href="http://www.it165.net/pro/webasp/" target="_blank" class="keylink">ASP</a>NETMvc.MockUnitTest.Bussiness;
       8:   
       9:  namespace Stephen.Sample.ASPNETMvc.MockUnitTest.TestProject
      10:  {
      11:      [TestClass]
      12:      public class MockUnitTest
      13:      {
      14:          private readonly Mock<IFoo> _fooMock; 
      15:   
      16:          public MockUnitTest()
      17:          {
      18:              _fooMock = new Mock<IFoo>();
      19:          }
      20:   
      21:          [TestMethod]
      22:          public void MockDoSomeThingMethodTest()
      23:          {
      24:              _fooMock.Setup(foo => foo.DoSomeThing(It.IsAny<string>())).Callback((string s) => Console.WriteLine(s));
      25:              _fooMock.Object.DoSomeThing("HelloWorld");
      26:          }
      27:   
      28:          [TestMethod]
      29:          public  void MockIsLoveMockFramewrokMethodTest()
      30:          {
      31:              _fooMock.Setup(foo => foo.IsLoveFoo()).Returns(true);
      32:              Assert.AreEqual(true, _fooMock.Object.IsLoveFoo());
      33:          }
      34:   
      35:          [TestMethod]
      36:          public  void MockFooNamePropertyTest()
      37:          {
      38:              _fooMock.Setup(foo => foo.FooName).Returns("stephen");
      39:              Assert.AreEqual("stephen",_fooMock.Object.FooName);
      40:          }
      41:      }
      42:  }

    第一个方法没有返回值,第二个有返回值分别用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的模拟

    首先创建一个控制器

       1:  using System;
       2:  using System.Collections.Generic;
       3:  using System.Linq;
       4:  using System.Text;
       5:  using System.Web.Mvc;
       6:  namespace Stephen.Sample.ASPNETMvc.MockUnitTest.Controller
       7:  {
       8:     public class HomeController:System.Web.Mvc.Controller
       9:      {
      10:        public ViewResult Index()
      11:        {
      12:            ViewData["controller"] = RouteData.Values["controller"];
      13:            ViewData["action"] = RouteData.Values["action"];
      14:            return View("Index");
      15:        }
      16:      }
      17:  }

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

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

       1:  using System;
       2:  using System.Text;
       3:  using System.Collections.Generic;
       4:  using System.Linq;
       5:  using System.Web.Mvc;
       6:  using Microsoft.VisualStudio.TestTools.UnitTesting;
       7:  using MvcContextMock;
       8:  using Stephen.Sample.ASPNETMvc.MockUnitTest.Controller;
       9:   
      10:  namespace Stephen.Sample.ASPNETMvc.MockUnitTest.TestProject
      11:  {
      12:      [TestClass]
      13:      public class HomeControllerUnitTest
      14:      {
      15:          [TestMethod]
      16:          public void IndexActionTest()
      17:          {
      18:              var homeController = new HomeController();
      19:              homeController.ControllerContext = MvcContextMock.MvcContextMockFactory.CreateControllerContext(homeController, "~/Home/Index", "get", "DefaultRoute", "{controller}/{action}", null);
      20:              ViewResult result= homeController.Index();
      21:              Assert.AreEqual("Index",result.ViewName);
      22:              Assert.AreEqual("Home",result.ViewData["controller"]);
      23:              Assert.AreEqual("Index", result.ViewData["action"]);
      24:          }
      25:      }
      26:  }

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

  • 相关阅读:
    graphite custom functions
    falcon适配ldap密码同步
    dell 远程管理卡的使用racadm
    mac 入门
    使用 kafkat 在线扩缩容 kafka replicas
    python收集jvm数据
    kafka java.rmi.server.ExportException: Port already in use
    centos6安装最新syslog-ng推送hdfs
    从 falcon api 中获取数据
    fluentd 推送 mariadb audit log
  • 原文地址:https://www.cnblogs.com/tukzer/p/3663412.html
Copyright © 2020-2023  润新知