[转]模拟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框 ...
随机推荐
- delete-node-in-a-bst
https://leetcode.com/problems/delete-node-in-a-bst/ /** * Definition for a binary tree node. * struc ...
- Android 实现布局动态加载
Android 动态加载布局 通过使用LayoutInflater 每次点击按钮时候去读取布局文件,然后找到布局文件里面的各个VIEW 操作完VIEW 后加载进我们setContentView 方面里 ...
- win8 iis安装及网站发布
win8 iis安装及网站发布 系统:win8 环境:vs2012 一:安装IIS 比较win7的安装来说,多选了几个钩钩,不然会报错,偶就遇到这样的错误. 控制面板->程序和功能->启动 ...
- 线程——QQ邮件发送
一.造一个QQ邮件发送的窗体 二.开始编写关于邮件发送的代码,以及当点发送按钮的时候,给发送按钮单独造了一个新的线程.这样如果发送的附件太多的话,如果不给发送按钮造新的线程,便会卡住,但是如果给发送按 ...
- laravel, Composer和autoloading
http://www.php-fig.org/psr/psr-4/ http://www.php-fig.org/psr/psr-0/ http://alanstorm.com/laravel_com ...
- HDU 4324 (拓扑排序) Triangle LOVE
因为题目说了,两个人之间总有一个人喜欢另一个人,而且不会有两个人互相喜欢.所以只要所给的图中有一个环,那么一定存在一个三元环. 所以用拓扑排序判断一下图中是否有环就行了. #include <c ...
- POJ 1037 (计数 + DP) 一个美妙的栅栏
这道题总算勉勉强强看懂了,DP和计数都很不好想 DP部分: 称i根木棒的合法方案集合为S(i),第二根木棒比第一根长的方案称作UP方案,反之叫做DOWN方案 C[i][k][DOWN] 是S(i)中以 ...
- Android init.rc文件格式解析
/***************************************************************************** * Android init.rc文件格式 ...
- acdream 1682 吃不完的糖果(环形最大子段和)
Problem Description 娜娜好不容易才在你的帮助下"跳"过了这个湖,果然车到山前必有路,大战之后必有回复,大难不死,必有后福!现在在娜娜面前的就是好多好多的糖果还有 ...
- 虚拟机安装centos 6 报错Erro processing drive
错误提示: Error processing drive: pci-0000:00:10-scsi-0:0:0:0 20480MB VMware,VMware Virtual S This devic ...