2011-04-27 180 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中解释的那样)。

除此之外 - 拦截器只是一个与接口交互的类 - 一切都是高度可测试的。