• 众所周知 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. apiCloud创建APP项目

    1.注册一个apiCloud账户 2.创建一个应用 3.安装sublime插件 4.用sublime创建应用 5.配置参数,保证一致 6.上传代码,两种方式 一种是压缩成zip 一种是配置svn,通过 ...

  2. Qt Linguist介绍

    简介 Qt提供了一款优秀的支持Qt C++和Qt Quick应用程序的翻译工具.发布者.翻译者和开发者可以使用这款工具来完成他们的任务. 发布者:承担了全面发布应用程序的责任.通常,他们协调开发者和翻 ...

  3. 使用Java API创建(create),查看(describe),列举(list),删除(delete)Kafka主题(Topic)

    使用Kafka的同学都知道,我们每次创建Kafka主题(Topic)的时候可以指定分区数和副本数等信息,如果将这些属性配置到server.properties文件中,以后调用Java API生成的主题 ...

  4. Warning: Using innodb_additional_mem_pool_size is DEPRECATED

    Warning: Using innodb_additional_mem_pool_size is DEPRECATED. This option may be removed in future r ...

  5. BZOJ 1078 斜堆

    感谢MATO大神的博客 http://www.cppblog.com/MatoNo1/archive/2013/03/03/192131.html 注意细节. #include<iostream ...

  6. Linux C double linked for any data type

    /************************************************************************** * Linux C double linked ...

  7. UVA 11383 Golden Tiger Claw 金虎爪(KM算法)

    题意: 给一个n*n的矩阵,每个格子中有正整数w[i][j],试为每行和每列分别确定一个数字row[i]和col[i],使得任意格子w[i][j]<=row[i]+col[j]恒成立.先输row ...

  8. php的类型约束

    //如下面的类 class MyClass { /** * 测试函数 * 第一个参数必须为 OtherClass 类的一个对象 */ public function test(OtherClass $ ...

  9. Java之路

    Java程序员 高级特性 反射.泛型.注释符.自动装箱和拆箱.枚举类.可变参数.可变返回类型.增强循环.静态导入 核心编程 IO.多线程.实体类.集合类.正则表达式.XML和属性文件 图形编程 AWT ...

  10. application tips

    trace(ApplicationDomain.currentDomain == ApplicationDomain.currentDomain); trace(stage.loaderInfo.ap ...