模拟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
原文地址:https://www.cnblogs.com/Leo_wl/p/2568872.html