• 众所周知 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. URAL1118. Nontrivial Numbers

    1118 优化 1.枚举到sqrt(n)2.区间有质数直接输出最大质数3.a=1 直接输出1 4.边+边与最小值比较 #include <iostream> #include<cst ...

  2. RIA技术

    Rich Internet Application(富互联网应用程序,简称RIA),一种全新的Web应用程序架构,它结合了桌面软件良好的用户体验和web应用程序易部署的优点,很快获得了企业的青睐. 近 ...

  3. JQuery_给元素添加或删除类等以及CSS()方法

    一.addClass() - 向被选元素添加一个或多个类 <script src="jquery-1.11.1.min.js"></script> < ...

  4. R语言中strptime返回值永远为NA的问题

    调用前加上以下代码,即可解决 Sys.setlocale("LC_TIME", "C");

  5. UVa 1479 (Treap 名次树) Graph and Queries

    这题写起来真累.. 名次树就是多了一个附加信息记录以该节点为根的树的总结点的个数,由于BST的性质再根据这个附加信息,我们可以很容易找到这棵树中第k大的值是多少. 所以在这道题中用一棵名次树来维护一个 ...

  6. HDU 3863 (博弈) No Gambling

    这是我见过的最简单的一道博弈了,其实不要被复杂的棋盘吓到了. 首先肯定会有而且仅有一个人胜,而且因为棋盘是对称的,所以先手相对于后手肯定更有优势,那么肯定是先手赢. 这是不是严格的推理,但是确实比较容 ...

  7. 浅析extendedLayout, automaticallyAdjustsScrollViewInsets, extendedLayoutIncludesOpaqueBars

    参考文章: http://stackoverflow.com/questions/18798792/explaining-difference-between-automaticallyadjusts ...

  8. python练习程序(c100经典例15)

    题目: 利用条件运算符的嵌套来完成此题:学习成绩〉=90分的同学用A表示,60-89分之间的用B表示,60分以下的用C表示. def foo(n): if n>=90: print 'A' , ...

  9. Java 中无参无返回值方法的使用

    如果方法不包含参数,且没有返回值,我们称为无参无返回值的方法. 方法的使用分两步: 第一步,定义方法 例如:下面代码定义了一个方法名为 show ,没有参数,且没有返回值的方法,执行的操作为输出 “ ...

  10. ASP.NET MVC之文件上传【一】

    前言 这一节我们来讲讲在MVC中如何进行文件的上传,我们逐步深入,一起来看看. Upload File(一) 我们在默认创建的项目中的Home控制器下添加如下: public ActionResult ...