最近项目中需要修改一个文件上传的.ashx处理,代码的大概形式是这样的:
public void ProcessRequest(HttpContext context) { CallA(context); }
方法CallA中直接使用了context并做了很多业务判断,也没有时间重构此方法,因此想对修改的代码做单元测试,
就需要Mock或实例化context参数。
提示:我们开发环境为vs2010,mock组件为moq,当然有条件使用moles或是mvc的情况下,做这个单元测试还是比较容易的
由于HttpContext是密封类,Moq就显得无力了,那就只有自己实例化了,从网上发现了有个帮助类,心中暗喜
在此感谢作者的无私的贡献。直到使用后发现一个问题,这个帮助类只支持对querystring,form,session的赋值,
而项目中却需要对context.Request.ServerVariables和context.Request.Files赋值(文件上传嘛,你懂的),
于是作为码农的我就开始堆代码了
下面是设置服务端参数的函数的实现:
public static void SetServerVar(HttpContext context, NameValueCollection vars) { System.Collections.Hashtable hash = new System.Collections.Hashtable(); Type entryType = typeof(System.Collections.Specialized.NameObjectCollectionBase).GetMethod("FindEntry", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance).ReturnType; Type p = typeof(System.Web.HttpApplication).Assembly.GetType("System.Web.HttpServerVarsCollectionEntry"); foreach (string key in vars) { var httpServerVarsCollectionEntry = PrivateInvoke.CreateInstanceNonPublicCtor(p, new object[] { key, vars[key] }); var entry = PrivateInvoke.CreateInstanceNonPublicCtor(entryType, new object[] { key, httpServerVarsCollectionEntry }); hash[key] = entry; } PrivateInvoke.SetNonPublicField(context.Request.ServerVariables, "_entriesTable", hash); PrivateInvoke.SetNonPublicField(context.Request.ServerVariables, "_allKeys", new string[] { "server_name" }); }
下面是设置上传文件的代码:
public static void SetFile(HttpContext context, string formName, string contentType, string fileName, byte[] file) { Type httpInputStream = typeof(HttpPostedFile).GetConstructors(System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance)[0].GetParameters()[2].ParameterType; Type httpRawUploadedContent = httpInputStream.GetConstructors(System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance)[0].GetParameters()[0].ParameterType; var row = PrivateInvoke.CreateInstanceNonPublicCtor(httpRawUploadedContent, new object[] { file.Length, file.Length }); PrivateInvoke.SetNonPublicField(row, "_data", file); PrivateInvoke.SetNonPublicField(row, "_length", file.Length); PrivateInvoke.SetNonPublicField(row, "_completed", true); var stream = PrivateInvoke.CreateInstanceNonPublicCtor(httpInputStream, new object[] { row, 0, file.Length }); HttpPostedFile f = (HttpPostedFile)PrivateInvoke.CreateInstanceNonPublicCtor(typeof(HttpPostedFile), new object[] { fileName, contentType, stream }); PrivateInvoke.InvokeNonPublicMethod(context.Request.Files, "AddFile", new object[] { formName, f }); }
代码我就不解释了,没什么技术含量。在此建议在写函数时尽量减少依赖性,如上面的CallA方法传入的是业务参数,也就没有这些问题了
最后,附上源码及demo,猛击我下载