[转]模拟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: }
.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }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.TestProject10: {11: [TestClass]12: public class MockUnitTest13: {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: }.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }
第一个方法没有返回值,第二个有返回值分别用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 MvcContextMock10: {11: /// <summary>12: /// Mvc Context 工厂13: /// </summary>14: public static class MvcContextMockFactory15: {16: private static ControllerContext controllerContext = null;17: /// <summary>18: /// 创建ControllerContext19: /// </summary>20: /// <param name="controller">Controller</param>21: /// <returns></returns>22: public static ControllerContext CreateControllerContext(Controller controller)23: {24: controllerContext = new ControllerContext25: (26: CreateHttpContext(),27: new RouteData(),28: controller);29: return controllerContext;30: }31:32: /// <summary>33: /// 创建ControllerContext34: /// </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 ControllerContext41: (42: contextBase,43: new RouteData(),44: controller);45: return controllerContext;46: }47:48:49: /// <summary>50: /// 创建ControllerContext51: /// </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 ControllerContext62: (63: CreateHttpContext(),64: GetRouteData(url, httpMethod, name, pattern, obj),65: controller);66: return controllerContext;67: }68:69: /// <summary>70: /// 创建ControllerContext71: /// </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 ControllerContext83: (84: contextBase,85: GetRouteData(url, httpMethod, name, pattern, obj),86: controller);87: return controllerContext;88: }89:90: /// <summary>91: /// 创建HttpContextBase92: /// </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: response102: .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 Method114: 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: response122: .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: #endregion145:146: }147: }.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }
三、在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.Controller7: {8: public class HomeController:System.Web.Mvc.Controller9: {10: public ViewResult Index()11: {12: ViewData["controller"] = RouteData.Values["controller"];13: ViewData["action"] = RouteData.Values["action"];14: return View("Index");15: }16: }17: }.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }
因为控制器中需要获取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.TestProject11: {12: [TestClass]13: public class HomeControllerUnitTest14: {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: }.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }
对于更简单的HttpContext在MockHttpContextFactory中有专用的方法生成与ControllerContext原理类似。
[转]模拟HttpContext 实现ASP.NET MVC 的单元测试的更多相关文章
- ASP.NET MVC之单元测试
ASP.NET MVC之单元测试分分钟的事2014-07-15 13:05 by 书洞里的猫, 550 阅读, 4 评论, 收藏, 编辑 一.为什么要进行单元测试? 大部分开发者都有个习惯(包括本人在 ...
- ASP.NET MVC编程——单元测试
1自动化测试基本概念 自动化测试分为:单元测试,集成测试,验收测试. 单元测试 检验被测单元的功能,被测单元一般为低级别的组件,如一个类或类方法. 单元测试要满足四个条件:自治的,可重复的,独立的,快 ...
- ASP.NET MVC之单元测试分分钟的事
一.为什么要进行单元测试? 大部分开发者都有个习惯(包括本人在内),常常不喜欢去做单元测试.因为我们对自己写的程序总是盲目自信,或者存在侥幸心理每次运行通过后就直接扔给测试组的妹子们了.结果妹子一测, ...
- asp.net mvc 5 单元测试小例子
using System.Collections.Generic; using System.Linq; using Microsoft.VisualStudio.TestTools.UnitTest ...
- asp.net mvc学习(Vs技巧与Httpcontext)
模型绑定分析 博客模拟的表单已经可以包含网站开发过程中遇到的大部分的表单格式了,包含一些数组.对象等等. 1.直接拼接字符串 $.ajax({ url: "/XXX", type: ...
- 一、ASP.NET MVC 路由(一)--- ASP.NET WebForm路由模拟
ASP.NET WebForm 应用,用户请求的是物理文件,其中包括静态页面和动态页面,在Url中的显示都是服务器中一个物理文件的相对路径.但是ASP.NET MVC就不同了,用户请求的是Contro ...
- ASP.NET MVC中HttpContext, HttpContextBase, HttpContextWrapper联系
ttpContext HttpContext是最原始的ASP.NET Context. MVC的目的之一是能够单元测试.HttpContext没有base class,并且不是virtual,所以不能 ...
- 【第三篇】ASP.NET MVC快速入门之安全策略(MVC5+EF6)
目录 [第一篇]ASP.NET MVC快速入门之数据库操作(MVC5+EF6) [第二篇]ASP.NET MVC快速入门之数据注解(MVC5+EF6) [第三篇]ASP.NET MVC快速入门之安全策 ...
- ASP.NET MVC Model绑定(六)
ASP.NET MVC Model绑定(六) 前言 前面的篇幅对于IValueProvider的使用做个基础的示例讲解,但是没并没有对 IValueProvider类型的实现做详细的介绍,然而MVC框 ...
随机推荐
- combobox远程加载数据的总结和Json数据的小结
1.从后台返回请求加载Combobox下拉框数据 html部分1 <select name="mateBelongZ" id="mateBelongZID" ...
- jquery ajax的async属性的理解
$(function(){ queryTemplateSort(); // fillAddTemplatePage(); function queryTemplateSort() { $.ajax({ ...
- Asp.Net生命周期系列三
上文讲到了HttpRunTime主要做了三个事情,我们先回忆一下. 第一:雇佣了项目经理(HttpApplication). 第二:建立了HttpModule列表,项目经理(HttpRunTime)就 ...
- POJ 2395 Out of Hay 草荒 (MST,Kruscal,最小瓶颈树)
题意:Bessie要从牧场1到达各大牧场去,他从不关心他要走多远,他只关心他的水袋够不够水,他可以在任意牧场补给水,问他走完各大牧场,最多的一次需要多少带多少单位的水? 思路:其实就是要让所带的水尽量 ...
- matlab注释使用,以及相应的注释快捷键
1.多行注释 单行注释是加% %{ 若干语句 %} 2.快捷键 多行注释: 选中要注释的若干语句, 编辑器菜单Text-<Comment, 或者快捷键Ctrl+R 取消注释: 选中要取消注释的语 ...
- php排序测试
对 http://www.cnblogs.com/kudosharry/articles/2521621.html 这个补充的调用系统sort()函数的测试结果 1000个随机数: 直接插入排序:时间 ...
- 【英语】Bingo口语笔记(62) - 生气道歉场景的表达
- [转载] FFmpeg API 变更记录
最近一两年内FFmpeg项目发展的速度很快,本来是一件好事.但是随之而来的问题就是其API(接口函数)一直在发生变动.这么一来基于旧一点版本的FFmpeg的程序的代码在最新的类库上可能就跑不通了. 例 ...
- Java循环语句之 for
Java 的循环结构中除了 while 和 do...while 外,还有 for 循环,三种循环可以相互替换. 语法: 执行过程: <1>. 执行循环变量初始化部分,设置循环的初始状态, ...
- IOS OC声明变量在@interface括号中与使用@property的区别(转载)
刚开始接触OC再看别人写的代码的时候,常常困惑于人家在声明属性时的写法,总结出来有三中方式,不知道哪一种比较规范化,现在我把三种方式贴出来,然后再一一探讨每个方式声明属性的区别. 方式一:直接在@in ...