模拟HttpContext单元测试

Posted 芜明-追星

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了模拟HttpContext单元测试相关的知识,希望对你有一定的参考价值。

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

一、Moq框架使用

Moq是一个非常优秀的模拟框架,可以实现对接口成员的模拟,常用在TDD中。

先来看一个简单的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();

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());

        }

        http://jiazu.wan128.cn

        [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");

      }

    }

}

 

因为控制器中需要获取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"]);

        }

    }

}

 

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

 

以上是关于模拟HttpContext单元测试的主要内容,如果未能解决你的问题,请参考以下文章

在测试初始化​​方法中模拟 HttpContext.Current

模拟 HttpContext 不起作用

为 .Net 单元测试设置 HttpContext

在单元测试中设置 HttpContext.Current.Session

对Controller的单元测试

单元测试 IHttpModule