单元测试不可测试那些类(无抽象、静态类、静态方法)
实际上“单元测试不可测试那些类(无抽象、静态类、静态方法)”是个伪命题,因为事实是:无抽象、静态类、静态方法都是不可单元测试的。那么,如果我们要写出可测试的代码,又要用到这些静态类等,该怎么办,实际上我们需要两个步骤:
1:为它们写一个包装类,让这个包装类是抽象的(继承自接口,或者抽象类,或者方法本身是Virtual的);
2:通知客户端程序员,使用包装类来代替原先的静态类来写业务逻辑;
实际上,微软也是这么干的,我在上一篇博文《 单元测试WebForm的UI逻辑及文件上传 》写到,最典型的不可测试类,那就是WebForm架构的网站中,对Response等的模拟。查看Response这个类:
namespace System.Web
{
public sealed class HttpResponse
{
...
}
}
很明显,如果我们在某个WebForm的后台方法中,直接使用它的话:
protected void Page_Load( object sender, EvengArgs e)
{
this .Response.Write( " test u " );
}
该后台代码逻辑就无法进行单元测试了,因为类似MOQ的框架所依赖的是代码本身具有可被重写行,如果某个类本身是静态的,就无法在运行时用模拟类替换掉实际类。
所以,写一个包装类吧,我们看到微软为Response写了一个包装类,为HttpResponseWrapper:
View Code
namespace System.Web
{
[TypeForwardedFrom( " System.Web.Abstractions, Version=3.5.0.0, Culture=Neutral, PublicKeyToken=31bf3856ad364e35 " )]
public class HttpResponseWrapper : HttpResponseBase
{
public override bool Buffer
{
get
{
}
set
{
}
}
public override bool BufferOutput
{
get
{
}
set
{
}
}
public override HttpCachePolicyBase Cache
{
get
{
}
}
public override string CacheControl
{
get
{
}
set
{
}
}
public override string Charset
{
get
{
}
set
{
}
}
public override CancellationToken ClientDisconnectedToken
{
get
{
}
}
public override Encoding ContentEncoding
{
get
{
}
set
{
}
}
public override string ContentType
{
get
{
}
set
{
}
}
public override HttpCookieCollection Cookies
{
get
{
}
}
public override int Expires
{
get
{
}
set
{
}
}
public override DateTime ExpiresAbsolute
{
get
{
}
set
{
}
}
public override Stream Filter
{
get
{
}
set
{
}
}
public override NameValueCollection Headers
{
get
{
}
}
public override Encoding HeaderEncoding
{
get
{
}
set
{
}
}
public override bool IsClientConnected
{
get
{
}
}
public override bool IsRequestBeingRedirected
{
get
{
}
}
public override TextWriter Output
{
get
{
}
set
{
}
}
public override Stream OutputStream
{
get
{
}
}
public override string RedirectLocation
{
get
{
}
set
{
}
}
public override string Status
{
get
{
}
set
{
}
}
public override int StatusCode
{
get
{
}
set
{
}
}
public override string StatusDescription
{
get
{
}
set
{
}
}
public override int SubStatusCode
{
get
{
}
set
{
}
}
public override bool SupportsAsyncFlush
{
get
{
}
}
public override bool SuppressContent
{
get
{
}
set
{
}
}
public override bool SuppressFormsAuthenticationRedirect
{
get
{
}
set
{
}
}
public override bool TrySkipIisCustomErrors
{
get
{
}
set
{
}
}
public HttpResponseWrapper(HttpResponse httpResponse)
{
}
public override void AddCacheItemDependency( string cacheKey)
{
}
public override void AddCacheItemDependencies(ArrayList cacheKeys)
{
}
public override void AddCacheItemDependencies( string [] cacheKeys)
{
}
public override void AddCacheDependency( params CacheDependency[] dependencies)
{
}
public override void AddFileDependency( string filename)
{
}
public override void AddFileDependencies(ArrayList filenames)
{
}
public override void AddFileDependencies( string [] filenames)
{
}
public override void AddHeader( string name, string value)
{
}
public override void AppendCookie(HttpCookie cookie)
{
}
public override void AppendHeader( string name, string value)
{
}
public override void AppendToLog( string param)
{
}
public override string ApplyAppPathModifier( string virtualPath)
{
}
public override IAsyncResult BeginFlush(AsyncCallback callback, object state)
{
}
public override void BinaryWrite( byte [] buffer)
{
}
public override void Clear()
{
}
public override void ClearContent()
{
}
public override void ClearHeaders()
{
}
public override void Close()
{
}
public override void DisableKernelCache()
{
}
public override void DisableUserCache()
{
}
public override void End()
{
}
public override void EndFlush(IAsyncResult asyncResult)
{
}
public override void Flush()
{
}
public override void Pics( string value)
{
}
public override void Redirect( string url)
{
}
public override void Redirect( string url, bool endResponse)
{
}
public override void RedirectPermanent( string url)
{
}
public override void RedirectPermanent( string url, bool endResponse)
{
}
public override void RedirectToRoute( object routeValues)
{
}
public override void RedirectToRoute( string routeName)
{
}
public override void RedirectToRoute(RouteValueDictionary routeValues)
{
}
public override void RedirectToRoute( string routeName, object routeValues)
{
}
public override void RedirectToRoute( string routeName, RouteValueDictionary routeValues)
{
}
public override void RedirectToRoutePermanent( object routeValues)
{
}
public override void RedirectToRoutePermanent( string routeName)
{
}
public override void RedirectToRoutePermanent(RouteValueDictionary routeValues)
{
}
public override void RedirectToRoutePermanent( string routeName, object routeValues)
{
}
public override void RedirectToRoutePermanent( string routeName, RouteValueDictionary routeValues)
{
}
public override void RemoveOutputCacheItem( string path)
{
}
public override void RemoveOutputCacheItem( string path, string providerName)
{
}
public override void SetCookie(HttpCookie cookie)
{
}
public override void TransmitFile( string filename)
{
}
public override void TransmitFile( string filename, long offset, long length)
{
}
public override void Write( string s)
{
}
public override void Write( char ch)
{
}
public override void Write( char [] buffer, int index, int count)
{
}
public override void Write( object obj)
{
}
public override void WriteFile( string filename)
{
}
public override void WriteFile( string filename, bool readIntoMemory)
{
}
public override void WriteFile( string filename, long offset, long size)
{
}
public override void WriteFile(IntPtr fileHandle, long offset, long size)
{
}
public override void WriteSubstitution(HttpResponseSubstitutionCallback callback)
{
}
}
}
光从代码本身的角度来说,可以说这个类什么事情也没做,但是它为我们提供了一个抽象体系(从抽象类HttpResponseBase继承),这样的话,我们的客户端代码就可以改写为:
protected HttpResponseBase _response;
protected void Page_Load( object sender, EvengArgs e)
{
_response.Write( " test u " );
}
OK,可测试了,因为我们可以在某个地方注入依赖给_response了。
注意,包装类的撰写,还可以使用接口,或者干脆也仅仅只是一个类,只要让被包装的这个同名的方法是virtual的就可以了。
另外,不可测试的那些类,可能往往已经作为稳定版本提交给客户了,我们就不能修改源代码然后告诉客户说你替换下我们的DLL吧,所以,为了让客户端程序员找到我们的包装类,可以让包装类以及被包装的类使用同一个命名空间。
本文出处: http://HdhCmsTestcnblogs测试数据/luminji/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接。
将本文分享到: QQ空间 新浪微博 人人网 开心网 搜狐微博 MSN 谷歌 更多 0
分类: 测试
作者: Leo_wl
出处: http://HdhCmsTestcnblogs测试数据/Leo_wl/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
版权信息查看更多关于单元测试不可测试那些类(无抽象、静态类、静态方法)的详细内容...