好得很程序员自学网

<tfoot draggable='sEl'></tfoot>

单元测试不可测试那些类(无抽象、静态类、静态方法)

单元测试不可测试那些类(无抽象、静态类、静态方法)

实际上“单元测试不可测试那些类(无抽象、静态类、静态方法)”是个伪命题,因为事实是:无抽象、静态类、静态方法都是不可单元测试的。那么,如果我们要写出可测试的代码,又要用到这些静态类等,该怎么办,实际上我们需要两个步骤:

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://www.cnblogs.com/luminji/  
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接。

 

将本文分享到: QQ空间 新浪微博 人人网 开心网 搜狐微博 MSN 谷歌 更多 0

 

 

分类:  测试

作者: Leo_wl

    

出处: http://www.cnblogs.com/Leo_wl/

    

本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

版权信息

查看更多关于单元测试不可测试那些类(无抽象、静态类、静态方法)的详细内容...

  阅读:45次