转载

Mock session,cookie,querystring in ASB.NET MVC

写测试用例的时候经常发现,所写的功能需要Http上下文的支持(session,cookie)这类的.

以下介绍2种应用场景.

用于控制器内Requet获取参数

控制器内的Requet其实是控制器内的属性.那么在mock的时候把那些上下文附加到Controller里的控制器上下文(ControllerContext )里,request自然就有东西了.


public Controller() { /// <summary> /// 获取或设置控制器上下文。 /// </summary> /// /// <returns> /// 控制器上下文。 /// </returns> public ControllerContext ControllerContext { get; set; } /// <summary> /// 为当前 HTTP 请求获取 HttpRequestBase 对象。 /// </summary> /// /// <returns> /// 请求对象。 /// </returns> public HttpRequestBase Request { get { if (this.HttpContext != null) return this.HttpContext.Request; return (HttpRequestBase) null; } } }

为此,为了单独的Mock这些http上下文中的一些元素,我们需要6个类

Mock类

//http://stephenwalther.com/archive/2008/07/01/asp-net-mvc-tip-12-faking-the-controller-context   public class FakeControllerContext : ControllerContext   {    //public FakeControllerContext(ControllerBase controller)    // : this(controller, null, null, null, null, null, null)    //{    //}    /// <summary>    /// MockCookie    /// </summary>    /// <param name="controller"></param>    /// <param name="cookies"></param>    public FakeControllerContext(ControllerBase controller, HttpCookieCollection cookies)     : this(controller, null, null, null, null, cookies, null)    {    }    /// <summary>    /// MockSession    /// </summary>    /// <param name="controller"></param>    /// <param name="sessionItems"></param>    public FakeControllerContext(ControllerBase controller, SessionStateItemCollection sessionItems)     : this(controller, null, null, null, null, null, sessionItems)    {    }    /// <summary>    /// MockForm    /// </summary>    /// <param name="controller"></param>    /// <param name="formParams"></param>    public FakeControllerContext(ControllerBase controller, NameValueCollection formParams)     : this(controller, null, null, formParams, null, null, null)    {    }    /// <summary>    /// MockForm+QueryString    /// </summary>    /// <param name="controller"></param>    /// <param name="formParams"></param>    /// <param name="queryStringParams"></param>    public FakeControllerContext(ControllerBase controller, NameValueCollection formParams, NameValueCollection queryStringParams)     : this(controller, null, null, formParams, queryStringParams, null, null)    {    }    public FakeControllerContext(ControllerBase controller, string userName)     : this(controller, userName, null, null, null, null, null)    {    }    public FakeControllerContext(ControllerBase controller, string userName, string[] roles)     : this(controller, userName, roles, null, null, null, null)    {    }    /// <summary>    /// Mock Session+Cookie+Form+QuertyString+IIdentity    /// </summary>    /// <param name="controller">控制器名</param>    /// <param name="userName"></param>    /// <param name="roles"></param>    /// <param name="formParams">Form</param>    /// <param name="queryStringParams">QueryString</param>    /// <param name="cookies">Cookie</param>    /// <param name="sessionItems">Session</param>    public FakeControllerContext     (      ControllerBase controller,      string userName,      string[] roles,      NameValueCollection formParams,      NameValueCollection queryStringParams,      HttpCookieCollection cookies,      SessionStateItemCollection sessionItems     )     : base(new FakeHttpContext(      new FakePrincipal(new FakeIdentity(userName), roles),      formParams,      queryStringParams,      cookies, sessionItems), new RouteData(), controller)    { }    /// <summary>    ///     /// </summary>    /// <param name="controller"></param>    /// <param name="formParams"></param>    /// <param name="queryStringParams"></param>    /// <param name="cookies"></param>    /// <param name="sessionItems"></param>    /// <param name="userName"></param>    /// <param name="roles"></param>    public FakeControllerContext   (    ControllerBase controller,    NameValueCollection formParams,    NameValueCollection queryStringParams,    HttpCookieCollection cookies,    SessionStateItemCollection sessionItems,    string userName = null,    string[] roles = null   )     : base(new FakeHttpContext(      new FakePrincipal(new FakeIdentity(userName), roles),      formParams,      queryStringParams,      cookies, sessionItems), new RouteData(), controller)    { }   }   public class FakeHttpContext : HttpContextBase   {    private readonly FakePrincipal _principal;    private readonly NameValueCollection _formParams;    private readonly NameValueCollection _queryStringParams;    private readonly HttpCookieCollection _cookies;    private readonly SessionStateItemCollection _sessionItems;    public FakeHttpContext(FakePrincipal principal, NameValueCollection formParams, NameValueCollection queryStringParams, HttpCookieCollection cookies, SessionStateItemCollection sessionItems)    {     _principal = principal;     _formParams = formParams;     _queryStringParams = queryStringParams;     _cookies = cookies;     _sessionItems = sessionItems;    }    public override HttpRequestBase Request { get { return new FakeHttpRequest(_formParams, _queryStringParams, _cookies); } }    public override IPrincipal User { get { return _principal; } set { throw new System.NotImplementedException(); } }    public override HttpSessionStateBase Session { get { return new FakeHttpSessionState(_sessionItems); } }   } public class FakeHttpRequest : HttpRequestBase   {    private readonly NameValueCollection _formParams;    private readonly NameValueCollection _queryStringParams;    private readonly HttpCookieCollection _cookies;    public FakeHttpRequest(NameValueCollection formParams, NameValueCollection queryStringParams, HttpCookieCollection cookies)    {     _formParams = formParams;     _queryStringParams = queryStringParams;     _cookies = cookies;    }    public override NameValueCollection Form    {     get     {      return _formParams;     }    }    public override NameValueCollection QueryString    {     get     {      return _queryStringParams;     }    }    public override HttpCookieCollection Cookies    {     get     {      return _cookies;     }    }   } public class FakeHttpSessionState : HttpSessionStateBase   {    private readonly SessionStateItemCollection _sessionItems;    public FakeHttpSessionState(SessionStateItemCollection sessionItems)    {     _sessionItems = sessionItems;    }    public override void Add(string name, object value)    {     _sessionItems[name] = value;    }    public override int Count    {     get     {      return _sessionItems.Count;     }    }    public override IEnumerator GetEnumerator()    {     return _sessionItems.GetEnumerator();    }    public override NameObjectCollectionBase.KeysCollection Keys    {     get     {      return _sessionItems.Keys;     }    }    public override object this[string name]    {     get     {      return _sessionItems[name];     }     set     {      _sessionItems[name] = value;     }    }    public override object this[int index]    {     get     {      return _sessionItems[index];     }     set     {      _sessionItems[index] = value;     }    }    public override void Remove(string name)    {     _sessionItems.Remove(name);    }   }   public class FakeIdentity : IIdentity   {    private readonly string _name;    public FakeIdentity(string userName) { _name = userName; }    public string AuthenticationType { get { throw new System.NotImplementedException(); } }    public bool IsAuthenticated { get { return !String.IsNullOrEmpty(_name); } }    public string Name { get { return _name; } }   }   public class FakePrincipal : IPrincipal   {    private readonly IIdentity _identity;    private readonly string[] _roles;    public FakePrincipal(IIdentity identity, string[] roles)    {     _identity = identity;     _roles = roles;    }    public IIdentity Identity { get { return _identity; } }    public bool IsInRole(string role)    {     if (_roles == null)      return false;     return _roles.Contains(role);    }   } 

在原示例里面那个外国佬还mock了其他东西( IPrincipal User).但对于我来说没这方面需求.

然后我们测试一下.

测试控制器

public class TestController : Controller   {    #region 请求模拟输出    public ActionResult TestSession()    {     return Content(Session["hehe"].ToString());    }    public ActionResult TestCookie()    {     var cookie = Request.Cookies["hehe"];     if (cookie == null)      return new EmptyResult();     return Content(cookie.Values["c1"]);    }    #endregion    #region 请求测试    public ActionResult TestForm()    {     string fuckyou = Request.Form["fuckyou"];     if (fuckyou == null)      return new EmptyResult();     return Content(fuckyou);    }    public ActionResult TestFormAndQueryString()    {     string form = Request.Form["fuckyou"];     string querty = Request.QueryString["fuckyou2"];     return Content(form + "," + querty);    }    public ActionResult TestMuilt()    {     var session = Session["hehe"].ToString();     var cookie = Request.Cookies["hehe"].Values["c1"];     string fuckyou = Request.Form["fuckyou"];     string querty = Request.QueryString["fuckyou2"];     return Content(string.Format("{1} {0} {2} {0}{3} {0} {4} {0}", Environment.NewLine, session, cookie, fuckyou, querty));    }    #endregion } 

测试类

[TestClass] public class MockRequestTest {  private readonly IUserCenterService _IUserCenterService;  public MockRequestTest()  {   EngineContext.Initialize(false);   _IUserCenterService = EngineContext.Current.Resolve<IUserCenterService>();  }  [Test]  [TestMethod]  public void MockSession()  {   //_IUserCenterService = EngineContext.Current.Resolve<IUserCenterService>();   var controller = new TestController();   var sessionItems = new SessionStateItemCollection();   sessionItems["hehe"] = 23;   controller.ControllerContext = new FakeControllerContext(controller, sessionItems);   var result = controller.TestSession() as ContentResult;   Assert.AreEqual(result.Content, "23");  }  [TestMethod]  public void MockCookie()  {   var controller = new TestController();   var mockCookie = new HttpCookie("hehe");   mockCookie["c1"] = "nima1";   mockCookie["c2"] = "nima2";   var requestCookie = new HttpCookieCollection() { { mockCookie } };   controller.ControllerContext = new FakeControllerContext(controller, requestCookie);   var result = controller.TestCookie() as ContentResult;   Console.WriteLine(HttpContext.Current == null);   Assert.AreEqual("nima1", result.Content);  }  /// <summary>  /// MockForm  /// </summary>  [TestMethod]  public void MockForm()  {   var controller = new TestController();   NameValueCollection form = new FormCollection()   {    {"fuckyou","1"},    {"fuckyou","2"},   };   controller.ControllerContext = new FakeControllerContext(controller, form);   var result = controller.TestForm() as ContentResult;   Debug.Assert(false, result.Content);   Assert.IsNotNull(result.Content);  }  /// <summary>  /// MockForm  /// </summary>  [TestMethod]  public void MockFormAndQueryString()  {   var controller = new TestController();   NameValueCollection form = new FormCollection()   {    {"fuckyou","1"},    {"fuckyou2","2"},   };   controller.ControllerContext = new FakeControllerContext(controller, form, form);   var result = controller.TestFormAndQueryString() as ContentResult;   //Debug.Assert(false, result.Content);   Assert.AreEqual("1,2", result.Content);  }  /// <summary>  /// Mock Session+Cookie+Form+QuertyString  /// </summary>  [TestMethod]  public void MockMuilt()  {   var controller = new TestController();   var sessionItems = new SessionStateItemCollection();   sessionItems["hehe"] = 23;   var mockCookie = new HttpCookie("hehe");   mockCookie["c1"] = "nima1";   mockCookie["c2"] = "nima2";   var requestCookie = new HttpCookieCollection() { { mockCookie } };   NameValueCollection form = new FormCollection()   {    {"fuckyou","1"},    {"fuckyou2","2"},   };   controller.ControllerContext = new FakeControllerContext(controller, form, form, requestCookie, sessionItems);   var result = controller.TestMuilt() as ContentResult;   Debug.Assert(    false,    result.Content,    string.Format("正确的结果顺序应该是{0};{1};{2};{3};", sessionItems[0], mockCookie["c1"], form["fuckyou"], form["fuckyou2"])    );  } } 

在上面这个MS测试用例里,我分别测试了

  • Mock session
  • Mock cookie
  • Mock表单
  • Mock 表单+querystring
  • Mock session+cookie+表单+querystring

都是通过的.

但是这样有个问题.

问题就是: 然而这并没有什么卵用.

mock HttpContext.Current

实际开发的时候.控制器基本打酱油,别的层面需要获取上下文是从HttpContext.Current.Request中获取.如果在刚才的测试用例.控制器输出的是HttpContext.Current.Request.这玩意无疑是null的.因为我们只是把上下文赋值到控制器里的http上下文里面,和HttpContext.Current.Reques是不同的一个概念.

所以呢,我们需要mock 和HttpContext.Current.Request.

session的话,比较容易,那就是

SessionStateUtility.AddHttpSessionStateToContext

cookie的话比较麻烦.HttpRequest.Cookies是一个只读属性,就算用反射赋值也会失败.这里我比较取巧,只用了cookie集合的第一个.有多个的话,可能得把方法改得更恶心一点吧.

代码

public static class WebExtension {  /// <summary>  /// 伪造session  /// </summary>  /// <param name="url"></param>  /// <param name="sesion"></param>  /// <param name="queryString"></param>  /// <param name="requesttype"></param>  public static void FakeHttpContext(this string url, SessionStateItemCollection sesion, string queryString = null, string requesttype = null, HttpCookieCollection cookie = null)  {   var stringWriter = new StringWriter();   var httpResponce = new HttpResponse(stringWriter);   HttpRequest request;   if (cookie == null)   {    request = new HttpRequest(string.Empty, url, queryString ?? string.Empty)      {       RequestType = requesttype ?? "GET",      };   }   else   {    request = new HttpRequest(string.Empty, url, queryString ?? string.Empty)    {     RequestType = requesttype ?? "GET",     Cookies = { cookie[0] },    };   }   var httpContext = new HttpContext(request, httpResponce);   if (sesion != null)   {    SessionStateUtility.AddHttpSessionStateToContext(httpContext,      new HttpSessionStateContainer(SessionNameStorage.Suser,        sesion,        new HttpStaticObjectsCollection(),        20000,        true,        HttpCookieMode.AutoDetect,        SessionStateMode.InProc,        false       ));   }   if (cookie != null)   {    //无法对只读属性赋值,会导致异常    //Type ret = typeof(HttpRequest);    //PropertyInfo pr = ret.GetProperty("Cookies");    //pr.SetValue(request, cookie, null); //赋值属性   }   //var sessionContainer = new HttpSessionStateContainer(   // "id",   // new SessionStateItemCollection(),   // new HttpStaticObjectsCollection(),   // 10,   // true,   // HttpCookieMode.AutoDetect,   // SessionStateMode.InProc,   // false);   //httpContext.Items["AspSession"] =   // typeof(HttpSessionState).GetConstructor(   //  BindingFlags.NonPublic | BindingFlags.Instance,   //  null,   //  CallingConventions.Standard,   //  new[] { typeof(HttpSessionStateContainer) },   //  null).Invoke(new object[] { sessionContainer });   HttpContext.Current = httpContext;  } } 

相应控制器以及测试用例

public ActionResult TestHttpCurrent() {  var a = System.Web.HttpContext.Current;  if (a != null)  {   return Content(a.Request.Cookies.Get("hehe").Value);  }  return Content(""); } [TestMethod] public void httpCurrent() {  var controller = new TestController();  var mockCookie = new HttpCookie("hehe");  mockCookie["c1"] = "nima1";  mockCookie["c2"] = "nima2";  var requestCookie = new HttpCookieCollection() { { mockCookie } };  string.Format("{0}/test/TestHttpCurrent", TestHelper.WebRootUrl).FakeHttpContext(sesion: null, cookie: requestCookie);  var result = controller.TestHttpCurrent() as ContentResult;  Console.WriteLine(result.Content); } 

session就不测了,我平时测试的时候试了无数次都是有的.

备注:

mock cookie那里,如果有更好的实现方式,请告诉我.

标题是故意为之的,代表了我对ASB.NET 的嘲讽.

参考链接:

ASP.NET MVC Tip #12 – Faking the Controller Context

ASP.NET MVC, HttpContext.Current is null while mocking a request

正文到此结束
Loading...