我们在开发WEB项目的时候,一般应用逻辑跟ASPX页面是分离的项目。应用逻辑一般会是一个DLL组件项目。如果这个组件项目中A方法使用了Session、Cookie等信息的读写,则这个方法就很难写单元测试。
  但并不是写不出来,要写出来大致思路如下:

  目标: 
  构建一个测试的环境,把需要的Session、Cookie等信息初始化好。 这样才好做测试。而且这个构建的环境,不应该影响实际功能代码的编写。

  具体实现来说:

  我们要使用Mock技术,但就HttpContext来言,直接mock这个对象会有一个问题,它不具备Session的功能。这时候我们就需要用 Mock 技术来构造一个可以满足我们需要的环境的原理:这个Mock的机制如下:

  用反射机制,构造一个 HttpSessionState 对象(HttpSessionState类的构造函数是internal 的),然后把这个对象跟SimpleWorkerRequest 对象捆绑。

这样我们就可以 构建了一个满足自己需要的环境了,即 TestHttpContext 类。

  以下是两个类的实现:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web.SessionState;
using System.Web;
using System.Threading;
using System.Globalization;
using System.Collections.Specialized;
using System.Collections;
using System.IO;
using System.Web.Hosting;
using System.Reflection;

namespace TestNamespace
{
  public class TestHttpContext
{
private const string ContextKeyAspSession = "AspSession";
private HttpContext context = null;
private TestHttpContext() : base() { }
public TestHttpContext(bool isSecure)
: this()
{
MySessionState myState = new MySessionState(Guid.NewGuid().ToString("N"),
new SessionStateItemCollection(), new HttpStaticObjectsCollection(),
5, true, HttpCookieMode.UseUri, SessionStateMode.InProc, false);

TextWriter tw = new StringWriter();
HttpWorkerRequest wr = new SimpleWorkerRequest("/webapp", "c:\\inetpub\\wwwroot\\webapp\\", "default.aspx", "", tw);
this.context = new HttpContext(wr);
HttpSessionState state = Activator.CreateInstance(
typeof(HttpSessionState),
BindingFlags.Public | BindingFlags.NonPublic |
BindingFlags.Instance | BindingFlags.CreateInstance,
null,
new object[] { myState },
CultureInfo.CurrentCulture) as HttpSessionState;
this.context.Items[ContextKeyAspSession] = state;
HttpContext.Current = this.context;
}

public HttpContext Context
{
get
{
return this.context;
}
}

private class WorkerRequest : SimpleWorkerRequest
{
private bool isSecure = false;
public WorkerRequest(string page, string query, TextWriter output, bool isSecure)
: base(page, query, output)
{
this.isSecure = isSecure;
}

public override bool IsSecure()
{
return this.isSecure;
}
}
}
public sealed class MySessionState : IHttpSessionState
{
const int MAX_TIMEOUT = 24 * 60; // Timeout cannot exceed 24 hours.

string pId;
ISessionStateItemCollection pSessionItems;
HttpStaticObjectsCollection pStaticObjects;
int pTimeout;
bool pNewSession;
HttpCookieMode pCookieMode;
SessionStateMode pMode;
bool pAbandon;
bool pIsReadonly;

public MySessionState(string id,
ISessionStateItemCollection sessionItems,
HttpStaticObjectsCollection staticObjects,
int timeout,
bool newSession,
HttpCookieMode cookieMode,
SessionStateMode mode,
bool isReadonly)
{
pId = id;
pSessionItems = sessionItems;
pStaticObjects = staticObjects;
pTimeout = timeout;
pNewSession = newSession;
pCookieMode = cookieMode;
pMode = mode;
pIsReadonly = isReadonly;
}

public int Timeout
{
get { return pTimeout; }
set
{
if (value <= 0)
throw new ArgumentException("Timeout value must be greater than zero.");

if (value > MAX_TIMEOUT)
throw new ArgumentException("Timout cannot be greater than " + MAX_TIMEOUT.ToString());

pTimeout = value;
}
}

public string SessionID
{
get { return pId; }
}

public bool IsNewSession
{
get { return pNewSession; }
}

public SessionStateMode Mode
{
get { return pMode; }
}

public bool IsCookieless
{
get { return CookieMode == HttpCookieMode.UseUri; }
}

public HttpCookieMode CookieMode
{
get { return pCookieMode; }
}

//
// Abandon marks the session as abandoned. The IsAbandoned property is used by the
// session state module to perform the abandon work during the ReleaseRequestState event.
//
public void Abandon()
{
pAbandon = true;
}

public bool IsAbandoned
{
get { return pAbandon; }
}

//
// Session.LCID exists only to support legacy ASP compatibility. ASP.NET developers should use
// Page.LCID instead.
//
public int LCID
{
get { return Thread.CurrentThread.CurrentCulture.LCID; }
set { Thread.CurrentThread.CurrentCulture = CultureInfo.ReadOnly(new CultureInfo(value)); }
}

//
// Session.CodePage exists only to support legacy ASP compatibility. ASP.NET developers should use
// Response.ContentEncoding instead.
//
public int CodePage
{
get
{
if (HttpContext.Current != null)
return HttpContext.Current.Response.ContentEncoding.CodePage;
else
return Encoding.Default.CodePage;
}
set
{
if (HttpContext.Current != null)
HttpContext.Current.Response.ContentEncoding = Encoding.GetEncoding(value);
}
}

public HttpStaticObjectsCollection StaticObjects
{
get { return pStaticObjects; }
}

public object this[string name]
{
get { return pSessionItems[name]; }
set { pSessionItems[name] = value; }
}

public object this[int index]
{
get { return pSessionItems[index]; }
set { pSessionItems[index] = value; }
}

public void Add(string name, object value)
{
pSessionItems[name] = value;
}

public void Remove(string name)
{
pSessionItems.Remove(name);
}

public void RemoveAt(int index)
{
pSessionItems.RemoveAt(index);
}

public void Clear()
{
pSessionItems.Clear();
}

public void RemoveAll()
{
Clear();
}

public int Count
{
get { return pSessionItems.Count; }
}

public NameObjectCollectionBase.KeysCollection Keys
{
get { return pSessionItems.Keys; }
}

public IEnumerator GetEnumerator()
{
return pSessionItems.GetEnumerator();
}

public void CopyTo(Array items, int index)
{
foreach (object o in items)
items.SetValue(o, index++);
}

public object SyncRoot
{
get { return this; }
}

public bool IsReadOnly
{
get { return pIsReadonly; }
}

public bool IsSynchronized
{
get { return false; }
}

}

}

这样我们在进行单元测试时就可以Mock掉Web下的Session,Cookie等对象。

例如:

[TestMethod()]
public void TestGetUserId()
{
TestHttpContext mock = new TestHttpContext(false);
System.Web.HttpContext context = mock.Context;
context.Session["UserId"] = 1245;

Assert.AreEqual(long.Parse(context.Session["UserId"].ToString()), 1245, "读取用户ID错误!");
}

单元测试写cookie的更多相关文章

  1. TDD中的单元测试写多少才够?

    测试驱动开发(TDD)已经是耳熟能详的名词,既然是测试驱动,那么测试用例代码就要写在开发代码的前面.但是如何写测试用例?写多少测试用例才够?我想大家在实际的操作过程都会产生这样的疑问. 3月15日,我 ...

  2. 跨域写cookie

    假设a站想往b站写cookie,那么目前有两种方案,参考如下: 第一种(使用jsonp): a站js代码如下: $.ajax({ url: 'http://www.b.com/jsonp.jsp?do ...

  3. PHP 跨域写cookie

    实际工作中,类似这样的要求很多,比如说,我们有两个域名,我们想实现在一个域名登录后,能自动完成另一个域名的登录,也就是PASSPORT的功能. 我只写一个大概,为了测试的方便,先编辑hosts文件,加 ...

  4. asp.net,cookie,写cookie,取cookie

    Cookie是一段文本信息,在客户端存储 Cookie 是 ASP.NET 的会话状态将请求与会话关联的方法之一.Cookie 也可以直接用于在请求之间保持数据,但数据随后将存储在客户端并随每个请求一 ...

  5. .NET,Cookie,写Cookie,取Cookie

    Cookie是一段文本信息,在客户端存储 Cookie 是 ASP.NET 的会话状态将请求与会话关联的方法之一.Cookie 也可以直接用于在请求之间保持数据,但数据随后将存储在客户端并随每个请求一 ...

  6. 怎么写cookie

    html结构 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UT ...

  7. asp.net,cookie,写cookie,取cookie(转载)

    Cookie是一段文本信息,在客户端存储 Cookie 是 ASP.NET 的会话状态将请求与会话关联的方法之一.Cookie 也可以直接用于在请求之间保持数据,但数据随后将存储在客户端并随每个请求一 ...

  8. tomcat 8.5 及其 9.0 response写cookie 设置damain为 [.test.com] 出错 An invalid domain [.test.com] was specified for this cookie

    抛出异常: java.lang.IllegalArgumentException: An invalid domain [.test.com] was specified for this cooki ...

  9. SpringBoot单元测试携带Cookie

    由于我SpringBoot项目,集成了SpringSecurity,而Security框架使用Redis存储Session,所以,这里列出几个关键的类 org.springframework.sess ...

随机推荐

  1. EPUB书籍阅读器插件分享

    本文主要分享EPUB文件的打开方式, 包括如何使用火狐浏览器打开epub, 如何使用chrome打开epub, 如何使用IE浏览器打开epub文件: 1:使用火狐打开epub文件 如果有安装火狐浏览器 ...

  2. mysql-窗口多表连接视图view

    create VIEW view_comment as (' as type FROM wei_comment_comment w) UNION ALL (' as type from review_ ...

  3. 4位组合型Excel文档密码怎么破解

    现代社会我们会遇到各种密码,很多的密码我们一段时间不用就不知不觉的忘记了.很多的excel用户就遇到过这种情况,这个时候我们就需要一款Excel密码破解工具.Advanced Office Passw ...

  4. 生活就像测试, BUG会越来越少,生活会越来越好!

    生活就像测试, BUG会越来越少,生活会越来越好!

  5. 【BZOJ-4386】Wycieczki DP + 矩阵乘法

    4386: [POI2015]Wycieczki Time Limit: 20 Sec  Memory Limit: 128 MBSubmit: 197  Solved: 49[Submit][Sta ...

  6. 【SDOI2010题集整合】BZOJ1922~1927&1941&1951&1952&1972&1974&1975

    BZOJ1922大陆争霸 思路:带限制的单源最短路 限制每个点的条件有二,路程和最早能进入的时间,那么对两个值一起限制跑最短路,显然想要访问一个点最少满足max(dis,time) 那么每次把相连的点 ...

  7. C#获取外网IP

    思路是通过WebRequest连接一些网上提供IP查询服务的网站,下载到含有你的IP的网页,然后用正则表达式提取出IP来 class Program { static void Main(string ...

  8. ffmpeg]ffmpeg使用参数的中文说明

    基本选项: -formats 输出所有可用格式 -f fmt 指定格式(音频或视频格式) -i filename 指定输入文件名,在linux下当然也能指定:0.0(屏幕录制)或摄像头 -y 覆盖已有 ...

  9. mysql 某周的起始和结束日期

    转自:http://bbs.csdn.net/topics/370096126 t_table有数据如下:year    Week2011    22011    32011    42011     ...

  10. BZOJ1588[HNOI2002]营业额统计

    传送门 平衡树常规题,给出两种实现算法 Treap版: //OJ 1610 //by Cydiater //2016.9.1 #include <iostream> #include &l ...