模拟HttpContext 实现ASP.NET MVC 的单元测试
众所周知 ASP.NET MVC 的一个显著优势即可以很方便的实现单元测试,但在我们测试过程中经常要用到HttpContext,而默认情况下单元测试框架是不提供HttpContext的模拟的,本文通过MOQ框架实现对HttpContext的模拟从而实现更便利的单元测试。
一、Moq框架使用
Moq是一个非常优秀的模拟框架,可以实现对接口成员的模拟,常用在TDD中。 可在此处下载 http://code.google测试数据/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
作者: Leo_wl
出处: http://HdhCmsTestcnblogs测试数据/Leo_wl/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
版权信息查看更多关于模拟HttpContext 实现ASP.NET MVC 的单元测试的详细内容...