先说场景。
为了保证业务逻辑跟页面展示分开。我们在开发WEB项目的时候,一般应用逻辑跟ASPX页面是分离的项目。
应用逻辑一般会是一个DLL组件项目。
如果这个组件项目中A方法使用了Session、Cookie等信息的读写,则这个方法就很难写单元测试。
但并不是写不出来,要写出来大致思路如下:
目标:
构建一个测试的环境,把需要的Session、Cookie等信息初始化好。 这样才好做测试。而且这个构建的环境,不应该影响实际功能代码的编写。
具体实现来说:
初始化的时候,使用了 HttpWorkerRequest 抽象类的派生类 SimpleWorkerRequest 来实现初始化。
但是 SimpleWorkerRequest 功能太简单了,不具备Session的功能。
这时候我们就需要用 Mock 技术来构造一个可以满足我们需要的环境的原理:
这个Mock的机制如下:
用反射机制,构造一个 HttpSessionState 对象(HttpSessionState类的构造函数是internal 的),然后把这个对象跟SimpleWorkerRequest 对象捆绑。
这样我们就可以 构建了一个满足自己需要的环境了,即 MockHttpContext 类。
具体实现的原理请参看:
http://www.jasonbock.net/JB/Default.aspx?blog=entry.161daabc728842aca6f329d87c81cfcb
但是那里是 .net 1.1 下的做法, .net 2.0 下很多发生变化了,后面整理的代码是 .net 2.0下该如何用的代码。
后面的演示把代码写的很简单,实际应用逻辑可能会很复杂。
比如我们在组件中有这样的一个方法,获得当前登录用户ID:
public static int GetMemberID()
{
string MemberID = HttpContext.Current.Session["MemberID"].ToString();
return Convert.ToInt32(MemberID);
}
我们下面就来考虑如何对这个方法进行单元测试。
因为是组件项目,我们无法使用 Visual Studio Team System 的ASP.net单元测试。
我们下面就用模拟的方式来做这个单元测试。
简单来说,就是我们需要构造一个HttpContext对象 ,然后在给其中的Session附值。然后再把它指定到 System.Web.HttpContext.Current 中。
先看一些基础知识:
有关在单元测试中使用HttpContext 的方法,请看下面的链接
Unit testing with HttpContext
http://weblogs.asp.net/jeff/archive/2004/05/31/145111.aspx
模拟的代码简单来说就是:
System.IO.TextWriter tw = new System.IO.StringWriter();
System.Web.HttpWorkerRequest wr = new System.Web.Hosting.SimpleWorkerRequest("/webapp", "c://inetpub//wwwroot//webapp//", "default.aspx", "", tw);
System.Web.HttpContext.Current = new System.Web.HttpContext(wr);
但是需要注意的是,如果我们再简单的增加下面一行,虽然编译没有错误,但是执行不通过。
System.Web.HttpContext.Current.Session["MemberID"] = 1245;
因为SimpleWorkerRequest并没有封装有关Session的功能。
这样我们需要作更多的东西。这就要用到Mock技术.
相关资料请参考:
Adding Session State to a Mock HttpContext Object
http://www.jasonbock.net/JB/Default.aspx?blog=entry.161daabc728842aca6f329d87c81cfcb
但是上面连接是给的.net 1.1下的做法。.net 2.0 下 HttpSessionState函数的构造函数发生了变化。那里的代码很多要发生变化。
.net 2.0 下 HttpSessionState 的构造函数变成了下面方式:
internal HttpSessionState(IHttpSessionState container)
{
this._container = container;
}
这样我们首先要实现一个 IHttpSessionState 接口的对象。
实现这个接口的范例代码请看:
http://msdn2.microsoft.com/zh-cn/library/system.web.sessionstate.ihttpsessionstate.aspx
其中有一个 MySessionState 就是实现了这个接口。
这个类在我们后面会被用到。
根据上述改变,我们根据
http://www.jasonbock.net/JB/Default.aspx?blog=entry.161daabc728842aca6f329d87c81cfcb
的知识点,作的 .net 2.0 下的 MockHttpContext 类如下:
public sealed class MockHttpContext
{
private const string ContextKeyAspSession = "AspSession";
private HttpContext context = null;
private MockHttpContext() : base() { }
public MockHttpContext(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;
}
}
}
至于单元测试,则很简单了,如下代码:
[TestMethod()]
public void TestGetMemberIDMethod()
{
MockHttpContext mock = new MockHttpContext(false);
System.Web.HttpContext context = mock.Context;
context.Session["MemberID"] = 1245;
int id = *****.GetMemberID();
Assert.AreEqual<int>(id, 1245, "读取用户ID错误!");
}
以上就是使用单元测试 测试组件中的含有Session等HttpContext功能调用的方法。