2013-10-30 65 views
3

事件我有一个简单的HTTP模块:如何嘲笑中的HttpModule

public class CustomLoggingModule : IHttpModule 
    { 
     public void Init(HttpApplication context) 
     { 
      context.BeginRequest += BeginRequest; 

      context.EndRequest += EndRequest; 
     } 

     public void BeginRequest(object sender, EventArgs eventArgs) 
     { 
      //some code 
     } 

     public void EndRequest(object sender, EventArgs eventArgs) 
     { 
      //some 
     } 

     public void Dispose() 
     { 
     } 
    } 

我怎么能单元测试呢?特别是如何嘲笑事件?任何人都可以举个简单的例子吗

+0

不知道什么试图在这里测试。没有太多的行为,它是基础设施。我建议留下您的HttpModule作为基础设施问题。将您认为需要单元测试的任何行为抽象掉到单独的类/服务和单元测试该行为。您可以在其他类型的测试中覆盖HttpModule,例如验收测试。 – Spock

+0

@Raj:感谢您的回复。其实我同意你的看法。在我的“BeginRequest”和“EndRequest”方法的处理程序中,我使用执行主要工作的“LogService”。所以我想在处理程序中测试'LogService'方法被调用。 – user2598794

+0

如何将LogService注入模块? – Spock

回答

5

不知道为什么你已经决定硬连线的依赖关系为新LogService()新HttpContextWrapper(HttpContext.Current)CustomLoggingModule内。如果要测试是否调用LogInfo()方法,如果可以将这些依赖关系外部化,以便注入残留/模拟版本等,则变得容易很多。

此外,您的问题并未声明您使用的是IOC container 。您可以register the HttpModule with the container并在运行时提供外部依赖关系。你的问题也没有说明使用isoloation/mock object framework。 因此,我将为您提供一种解决方案,您可以使用手写存根和模拟来验证是否调用LogInfo方法。

为了实现这一点,我们需要重构CustomLoggingModule有点,所以它变得更加可测试。

被测系统(SUT)

public class CustomLoggingModule : IHttpModule 
{ 
    public ILogService LogService { get; set; } 
    public Func<ILoggingHttpContextWrapper> LogginHttpContextWrapperDelegate { get; set; } 

    public void Init(HttpApplication context) { 
     context.BeginRequest += BeginRequest; 
     context.EndRequest += EndRequest; 
    } 

    public CustomLoggingModule() { 
     LogginHttpContextWrapperDelegate =() => new LoggingHttpContextWrapper(); 
    } 

    public void BeginRequest(object sender, EventArgs eventArgs) { 
     LogService.LogInfo(LogginHttpContextWrapperDelegate().HttpContextWrapper); 
    } 

    public void EndRequest(object sender, EventArgs eventArgs) { 
     //some 
    } 

    public void Dispose(){ } 
} 

正如你看到的上面,我已经介绍了2点额外的属性 - ILogService所以我可以提供一个嘲笑其它版本并委托Func键这让我来存根 新的HttpContextWrapper(HttpContext.Current);

public interface ILoggingHttpContextWrapper { 
    HttpContextWrapper HttpContextWrapper { get; } 
} 

public class LoggingHttpContextWrapper : ILoggingHttpContextWrapper 
{ 
    public LoggingHttpContextWrapper() { 
     HttpContextWrapper = new HttpContextWrapper(HttpContext.Current); 
    } 

    public HttpContextWrapper HttpContextWrapper { get; private set; } 
} 

然后你真正ILogService

public interface ILogService { 
    void LogInfo(HttpContextWrapper httpContextWrapper); 
} 

public class LogService : ILogService { 
    public void LogInfo(HttpContextWrapper httpContextWrapper) 
    { 
     //real logger implementation  
    } 
} 

单元测试:

您可以创建一个MockLoggerService,这样你就可以验证交互I,E是否LOGINFO()方法称为等等。您还需要一个存根LoggingHttpContextWrapper将假的HttpContextWrapper提供给SUT(被测系统)/ CustomLoggingModule。

public class StubLoggingHttpContextWrapper : ILoggingHttpContextWrapper 
{ 
    public StubLoggingHttpContextWrapper(){} 

    public HttpContextWrapper HttpContextWrapper { get; private set; } 
} 

public class MockLoggerService : ILogService 
{ 
    public bool LogInfoMethodIsCalled = false; 
    public void LogInfo(HttpContextWrapper httpContextWrapper) { 
     LogInfoMethodIsCalled = true; 
    } 
} 

MockLoggerService非常重要。这不是真正的记录器服务,但它是嘲笑的版本。当我们做公共类MockLoggerService:ILogService这意味着我们正在为记录器服务提供另一个间接层,以便我们验证行为的交互。

您还注意到我已经提供了一个布尔变量来验证是否调用LogInfo方法。这允许我从SUT调用此方法,并验证是否调用该方法。

现在您的单元测试可以实现如下。

[TestMethod] 
    public void CustomLoggingModule_BeginRequest_VerifyLogInfoMethodIsCalled() 
    { 
     var sut = new CustomLoggingModule(); 
     var loggerServiceMock = new MockLoggerService(); 
     var loggingHttpContextWrapperStub = new StubLoggingHttpContextWrapper(); 

     sut.LogService = loggerServiceMock; 
     sut.LogginHttpContextWrapperDelegate =() => loggingHttpContextWrapperStub; 

     sut.BeginRequest(new object(), new EventArgs()); 

     Assert.IsTrue(loggerServiceMock.LogInfoMethodIsCalled); 
    } 
0

我有同样的问题,我的自定义HTTP模块,并决定,我不会轻易放弃,并会尽我所能来触发单元测试BeginRequest事件。我不得不通读HttpApplication类的源代码并使用反射来调用该方法。

[TestMethod] 
    public void EventTriggered_DoesNotError() 
    { 
     using (var application = new HttpApplication()) 
     { 
      var module = new CustomLoggingModule(); 
      module.Init(application); 

      FireHttpApplicationEvent(application, "EventBeginRequest", this, EventArgs.Empty); 
     } 
    } 

    private static void FireHttpApplicationEvent(object onMe, string invokeMe, params object[] args) 
    { 
     var objectType = onMe.GetType(); 

     object eventIndex = (object)objectType.GetField(invokeMe, System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic).GetValue(onMe); 
     EventHandlerList events = (EventHandlerList)objectType.GetField("_events", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic).GetValue(onMe); 

     EventHandler handler = (EventHandler)events[eventIndex]; 

     Delegate[] delegates = handler.GetInvocationList(); 

     foreach (Delegate dlg in delegates) 
     { 
      dlg.Method.Invoke(dlg.Target, args); 
     } 
    } 
+0

一个非常好的尝试,当测试命中方法本身,例如'EventBeginRequest(object sender,EventArgs e)'发送者类型是测试类(在我的情况下是'{ProxyV2ConverterHttpModule.Tests.XForwardedForRewriterTests'''而不是'System.Web.HttpApplication '所以一切都失败了,仍然是一个不错的尝试! –