• 众所周知 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. mysql数据库导入外键约束问题

    在网站搬迁过程中,很重要一点是数据的迁移.你的数据库可能已经包含了一个设计良好的数据表集合,并且在网站运营过程中,产生了重要的数据.这时你必须做好包含数据表schema以及数据本身的迁移. 完成上述数 ...

  2. Bundle对象的使用

    在Android开发中,如果要通过一个Activity启动另外一个Activity,需要调用startActivity()函数,这个函数的参数是一个Intent对象,这个对象通常的初始化方式如下: I ...

  3. 51nod1120 机器人走方格 V3

    跟括号序列是一样的,将向右走看成是左括号向左走看成是右括号就可以了.那么就是卡特兰数了.然后由于n和m太大所以用了lucas定理 //跟括号序列是一样的,将向右走看成是左括号向左走看成是右括号就可以了 ...

  4. 属性readwrite,readonly,assign,retain,copy,nonatomic

    copy:建立一个索引计数为1的对象,然后释放旧对象 对NSString对NSString 它指出,在赋值时使用传入值的一份拷贝.拷贝工作由copy方法执行,此属性只对那些实行了NSCopying协议 ...

  5. curl文件上传有两种方式,一种是post_fileds,一种是infile

    curl文件上传有两种方式,一种是POSTFIELDS,一种是INFILE,POSTFIELDS传递@实际地址,INFILE传递文件流句柄! );curl_setopt($ch, CURLOPT_PO ...

  6. Windows下PHP+Eclipse开发环境搭建 及错误解决(apache2.2服务无法启动 发生服务特定错误:1)

    前言 Eclipse与php/apache的关系:Eclipse只是用来写代码的,如果想要在浏览器查看运行效果就要让php/apache的运行目录指向你的代码目录.Eclipse貌似不会自己和apac ...

  7. ylb:创建数据库、表,对表的增查改删语句

    ylbtech-SQL Server:SQL Server-创建数据库.表,对表的增查改删语句 SQL Server 创建数据库.表,对表的增查改删语句. 1,ylb:创建数据库.表,对表的增查改删语 ...

  8. SPF详解

    什么是SPF? 这里的SPF不是防晒指数,而是指Sender Policy Framework.翻译过来就是发信者策略架构,比较拗口,通常都直接称为SPF. SPF是跟DNS相关的一项技术,它的内容写 ...

  9. When not to automate 什么时候不进行自动化

    The cornerstone of test automation is the premise that the expected application behavior is known. W ...

  10. JavaScript专业规则12条

    学习JavaScript是困难的.它发展的如此之快,以至于在任何一个特定的时刻,你都不清楚自己是否“做错了”.有些时候,感觉像是坏的部分超过了好的部分.然而,讨论这些并没有意义,JavaScript正 ...