2011-04-27 181 views
4

我想寫一些攔截器的單元測試,攔截器攔截基類(實現ILoggable)。
可記錄基類沒有可調用的方法,僅用於由日誌工具初始化。
據我瞭解我應該:如何單元測試攔截器?

  1. 模擬的ILoggableILogger
  2. 初始化日誌工具
  3. 註冊就可以了我的攔截
  4. 調用嘲笑ILoggable的一些方法

問題是我的接口沒有方法調用,因此沒有任何東西會被攔截。
在這裏採取行動的正確方法是什麼?
我應該嘲笑ILoggable手動並添加存根方法調用?
另外,我是否應該嘲笑容器?

我正在使用Moq和NUnit。
編輯:
這是我參考攔截器實現:

public class LoggingWithDebugInterceptor : IInterceptor 
{ 
    #region IInterceptor Members 

    public void Intercept(IInvocation invocation) 
    { 
     var invocationLogMessage = new InvocationLogMessage(invocation); 

     ILoggable loggable = invocation.InvocationTarget as ILoggable; 

     if (loggable == null) 
      throw new InterceptionFailureException(invocation, string.Format("Class {0} does not implement ILoggable.", invocationLogMessage.InvocationSource)); 

     loggable.Logger.DebugFormat("Method {0} called with arguments {1}", invocationLogMessage.InvokedMethod, invocationLogMessage.Arguments); 

     Stopwatch stopwatch = new Stopwatch(); 
     try 
     { 
      stopwatch.Start(); 
      invocation.Proceed(); 
      stopwatch.Stop(); 
     } 
     catch (Exception e) 
     { 
      loggable.Logger.ErrorFormat(e, "An exception occured in {0} while calling method {1} with arguments {2}", invocationLogMessage.InvocationSource, invocationLogMessage.InvokedMethod, invocationLogMessage.Arguments); 
      throw; 
     } 
     finally 
     { 
      loggable.Logger.DebugFormat("Method {0} returned with value {1} and took exactly {2} to run.", invocationLogMessage.InvokedMethod, invocation.ReturnValue, stopwatch.Elapsed); 
     } 
    } 

    #endregion IInterceptor Members 
} 

回答

4

我同意Krzysztof,如果您希望通過AOP添加日誌記錄,關於日誌記錄的責任和實施細節應該對調用者透明。因此這是Interceptor可以擁有的東西。我會試着概述我將如何測試這個。

如果我正確地回答了這個問題,那麼您的ILoggable實際上只是一個註釋類的命名容器,以便攔截器可以確定它是否應該執行日誌記錄。它公開了一個包含Logger的屬性。(這樣做的缺點是,該類仍然需要配置記錄儀)。

public interface ILoggable 
{ 
    ILogger { get; set; } 
} 

測試攔截應該是一個簡單的過程。我看到您提出的唯一挑戰是如何手動構建輸入參數,以使其類似於運行時數據。我不建議通過mock等來重現這一點,我建議你使用傳統的基於狀態的驗證來測試它:創建一個使用攔截器的代理並驗證你的日誌反映了你的期望。

這看起來可能有點多,但它提供了一個很好的例子,說明攔截器如何獨立於代碼庫的其他部分工作。您的團隊中的其他開發人員可以從中受益,因爲他們可以將此示例作爲學習工具參考。

public class TypeThatSupportsLogging : ILoggable 
{ 
    public ILogger { get; set; } 

    public virtual void MethodToIntercept() 
    { 
    } 

    public void MethodWithoutLogging() 
    { 
    } 
} 

public class TestLogger : ILogger 
{ 
    private StringBuilder _output; 

    public TestLogger() 
    { 
     _output = new StringBuilder(); 
    } 

    public void DebugFormat(string message, params object[] args) 
    { 
     _output.AppendFormat(message, args); 
    } 

    public string Output 
    { 
     get { return _output.ToString(); } 
    } 
} 

[TestFixture] 
public class LoggingWithDebugInterceptorTests 
{ 
    protected TypeThatSupportsLogging Input; 
    protected LoggingWithDebugInterceptor Subject; 
    protected ILogger Log;   

    [Setup] 
    public void Setup() 
    { 
     // create your interceptor 
     Subject = new LoggingWithDebugInterceptor(); 

     // create your proxy 
     var generator = new Castle.DynamicProxy.ProxyGenerator(); 
     Input = generator.CreateClassProxy<TypeThatSupportLogging>(Subject); 

     // setup the logger 
     Log = new TestLogger(); 
     Input.Logger = Log; 
    } 

    [Test] 
    public void DemonstrateThatTheInterceptorLogsInformationAboutVirtualMethods() 
    { 
      // act 
      Input.MethodToIntercept(); 

      // assert 
      StringAssert.Contains("MethodToIntercept", Log.Output); 
    } 

    [Test] 
    public void DemonstrateNonVirtualMethodsAreNotLogged() 
    { 
      // act 
      Input.MethodWithoutLogging(); 

      // assert 
      Assert.AreEqual(String.Empty, Log.Output); 
    } 
} 
0

沒有方法?你在測試什麼?

就個人而言,這聽起來好像太過分了。我意識到TDD和代碼覆蓋是教條,但是如果你嘲笑一個沒有方法的接口,並且證明模擬框架完成了你指示它做的事情,那麼你真正證明了什麼?

這裏還有另一個錯誤:logging是面向方面編程的「hello world」。你爲什麼不在攔截器/方面進行登錄?如果你這樣做,那麼沒有理由讓你的所有課程實施ILoggable;你可以用聲明性的日誌記錄功能來裝飾它們。我認爲這是一種侵入性較小的設計和更好地使用攔截器。

+0

我測試攔截器的實現。我想檢查它做它應該做什麼。如果您建議使用屬性來攔截可記錄的方法,那麼我不明白記錄工具存在的原因。查看代碼以瞭解測試的內容。 – 2011-04-27 11:47:34

6

如果它只是在你的班級上使用Logger屬性的攔截器,而不是爲什麼在那裏存在?你也可以把它放在攔截器上。 (就像Ayende在他的post here中解釋的那樣)。

除此之外 - 攔截器只是一個與接口交互的類 - 一切都是高度可測試的。