• 众所周知 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.TestProject
  10:  {
  11:      [TestClass]
  12:      public class MockUnitTest
  13:      {
  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 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:   }

.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.Controller
   7:  {
   8:     public class HomeController:System.Web.Mvc.Controller
   9:      {
  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.TestProject
  11:  {
  12:      [TestClass]
  13:      public class HomeControllerUnitTest
  14:      {
  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 的单元测试的更多相关文章

  1. ASP.NET MVC之单元测试

    ASP.NET MVC之单元测试分分钟的事2014-07-15 13:05 by 书洞里的猫, 550 阅读, 4 评论, 收藏, 编辑 一.为什么要进行单元测试? 大部分开发者都有个习惯(包括本人在 ...

  2. ASP.NET MVC编程——单元测试

    1自动化测试基本概念 自动化测试分为:单元测试,集成测试,验收测试. 单元测试 检验被测单元的功能,被测单元一般为低级别的组件,如一个类或类方法. 单元测试要满足四个条件:自治的,可重复的,独立的,快 ...

  3. ASP.NET MVC之单元测试分分钟的事

    一.为什么要进行单元测试? 大部分开发者都有个习惯(包括本人在内),常常不喜欢去做单元测试.因为我们对自己写的程序总是盲目自信,或者存在侥幸心理每次运行通过后就直接扔给测试组的妹子们了.结果妹子一测, ...

  4. asp.net mvc 5 单元测试小例子

    using System.Collections.Generic; using System.Linq; using Microsoft.VisualStudio.TestTools.UnitTest ...

  5. asp.net mvc学习(Vs技巧与Httpcontext)

    模型绑定分析 博客模拟的表单已经可以包含网站开发过程中遇到的大部分的表单格式了,包含一些数组.对象等等. 1.直接拼接字符串 $.ajax({ url: "/XXX", type: ...

  6. 一、ASP.NET MVC 路由(一)--- ASP.NET WebForm路由模拟

    ASP.NET WebForm 应用,用户请求的是物理文件,其中包括静态页面和动态页面,在Url中的显示都是服务器中一个物理文件的相对路径.但是ASP.NET MVC就不同了,用户请求的是Contro ...

  7. ASP.NET MVC中HttpContext, HttpContextBase, HttpContextWrapper联系

    ttpContext HttpContext是最原始的ASP.NET Context. MVC的目的之一是能够单元测试.HttpContext没有base class,并且不是virtual,所以不能 ...

  8. 【第三篇】ASP.NET MVC快速入门之安全策略(MVC5+EF6)

    目录 [第一篇]ASP.NET MVC快速入门之数据库操作(MVC5+EF6) [第二篇]ASP.NET MVC快速入门之数据注解(MVC5+EF6) [第三篇]ASP.NET MVC快速入门之安全策 ...

  9. ASP.NET MVC Model绑定(六)

    ASP.NET MVC Model绑定(六) 前言 前面的篇幅对于IValueProvider的使用做个基础的示例讲解,但是没并没有对 IValueProvider类型的实现做详细的介绍,然而MVC框 ...

随机推荐

  1. HDU 2553 (状压) N皇后问题 (2)

    也许大多数做法都是打表,但这里用位运算的思想来解决这个问题,位运算果然强大,Orz 原文地址,感觉讲的很明白了: http://www.cnblogs.com/gj-Acit/archive/2013 ...

  2. LA 3695 Distant Galaxy

    给出n个点的坐标(坐标均为正数),求最多有多少点能同在一个矩形的边界上. 题解里是构造了这样的几个数组,图中表示的很明白了. 首先枚举两条水平线,然后left[i]表示竖线i左边位于水平线上的点,on ...

  3. [转] POJ DP问题

    列表一:经典题目题号:容易: 1018, 1050, 1083, 1088, 1125, 1143, 1157, 1163, 1178, 1179, 1189, 1191,1208, 1276, 13 ...

  4. 新版Windows Azure CDN管理门户正式上线

    经过产品团队的不懈努力,新版Windows Azure CDN管理门户在经过了有限开放预览之后,已经正式上线并开放给所有用户. 新版Windows Azure CDN管理门户经过全新的设计,除了在使用 ...

  5. python练习程序(c100经典例18)

    题目: 求s=a+aa+aaa+aaaa+aa...a的值,其中a是一个数字.例如2+22+222+2222+22222(此时共有5个数相加),几个数相加有键盘控制. def foo(a,n): su ...

  6. scala学习笔记(1):基本语法与容器

    1 var 可变,val 不可变 var (a,b) = (10,20) 分别对a,b赋值 a=10, b=20 var a,b = (10,20)则 是a=(10,20) b=(10,20) 2 L ...

  7. 【英语】Bingo口语笔记(72) - play系列

  8. 湖大 11404 manacher

    链接   http://acm.hunnu.edu.cn/online/?action=problem&type=show&id=11404&courseid=0 求 最长回文 ...

  9. 剑指offer—第三章高质量代码(o(1)时间删除链表节点)

    题目:给定单向链表的头指针和一个节点指针,定义一个函数在O(1)时间删除该节点,链表节点与函数的定义如下:struct ListNode{int m_nValue;ListNode* m_pValue ...

  10. 【转】Github轻松上手5-站在巨人的肩膀上(Fork)

    转自:http://blog.sina.com.cn/s/blog_4b55f6860100zzj3.html 有时候你可能想给别人的项目出把力,或者想以别人的项目作为自己项目的起点,在Github里 ...