Unit testing 如何在HttpModule中模拟事件
我有一个简单的Http模块:Unit testing 如何在HttpModule中模拟事件,unit-testing,mocking,httpmodule,Unit Testing,Mocking,Httpmodule,我有一个简单的Http模块: public class CustomLoggingModule : IHttpModule { public void Init(HttpApplication context) { context.BeginRequest += BeginRequest; context.EndRequest += EndRequest; } public
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()
{
}
}
如何进行单元测试?尤其是如何模拟事件?有人能举一些简单的例子吗?不知道为什么您决定在CustomLoggingModule中将依赖项硬连接为new LogService()和new HttpContextWrapper(HttpContext.Current)。如果您想测试是否调用了LogInfo()方法,那么如果您可以将这些依赖项外部化,从而可以注入存根/模拟版本等,就变得容易多了 此外,您的问题没有说明您正在使用。您可以在运行时创建和提供外部依赖项。您的问题也没有说明使用。 因此,我将为您提供一个解决方案,您可以使用手写存根和模拟来验证是否调用了LogInfo方法 为了实现这一点,我们需要稍微重构CustomLoggingModule,使其更易于测试 被测系统(SUT) 然后是你真正的ILogService
public interface ILogService {
void LogInfo(HttpContextWrapper httpContextWrapper);
}
public class LogService : ILogService {
public void LogInfo(HttpContextWrapper httpContextWrapper)
{
//real logger implementation
}
}
单元测试:
您将创建一个MockLoggerService,以便验证交互,即是否调用了LogInfo()方法等。您还需要一个存根LoggingHttpContextWrapper来向SUT(测试中的系统)/CustomLoggingModule提供假HttpContextWrapper
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: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);
}
我的自定义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);
}
}
不确定这里要测试什么。没有太多的行为,这是基础设施。我建议将您的HttpModule作为一个基础架构问题。将您认为需要单元测试的任何行为抽象到单独的类/服务中,并对该行为进行单元测试。您可以在其他类型的测试中介绍HttpModule,例如验收测试。@Raj:谢谢您的回复。事实上我同意你的看法。在“BeginRequest”和“EndRequest”方法中的处理程序中,我使用执行主作业的“LogService”。因此,我想测试在处理程序中是否调用了“LogService”方法。如何将LogService注入模块?与往常一样。下面是BeginRequest方法中的内容:
ILogService service=new LogService();LogInfo(新的HttpContextWrapper(HttpContext.Current))代码>thx-更新了下面的答案。这是一个非常好的尝试,不幸的是当测试命中方法本身时,例如EventBeginRequest(objectsender,EventArgs e)
发送者类型是测试类(在我的例子中{ProxyV2ConverterHttpModule.Tests.XForwardedForRewriterTests}
)而不是System.Web.HttpApplication
,所以从这一点上来说,一切都失败了,这仍然是一次很好的尝试!对我来说只要稍加修改就行了干杯
[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);
}
[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);
}
}