含有HttpContext元素的单元测试

    我们在开发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错误!");
}

posted @ 2010-10-18 13:30  Wang Juqiang  阅读(2422)  评论(2编辑  收藏  举报