我正在实现IHttpModule
,并试图为此编写单元测试(使用NUnit和Moq)。我在模拟Init
方法的HttpApplication
依赖项时遇到问题:
void Init(HttpApplication context);
通常,ASP.NET控制HttpApplication
实例并将其传递给Init
方法。在Init
方法中,自定义IHttpModule
订阅由HttpApplication
实例发布的事件(例如BeginRequest
和EndRequest
)。
我需要某种方法来模拟HttpApplication
,以便引发事件并测试IHttpModule
事件处理程序的实现是否正常。
我已经在测试中尝试创建模拟HttpApplication
:
// Mock for the logging dependency
var mockLogger = new Mock<ILogger>();
// My attempt at mocking the HttpApplication
var mockApplication = new Mock<HttpApplication>();
// MyModule is my class that implements IHttpModule
var myModule = new MyModule(mockLogger.Object);
// Calling Init, which subscribes my event handlers to the HttpApplication events
myModule.Init(mockApplication.Object);
// Attempting to raise the begin and end request events
mockApplication.Raise(a => a.BeginRequest += null, EventArgs.Empty);
mockApplication.Raise(a => a.EndRequest += null, EventArgs.Empty);
// RequestTime is a long property that tracks the time it took (in miliseconds) for a
// request to be processed and is set in the event handler subscribed to EndRequest
Assert.Greater(myModule.RequestTime, 0);
...但是它给出了以下错误消息:
表达式不是事件的附加或分离,或者事件是在类中声明的,但未标记为虚拟。
当我查看该错误时,我了解到Moq只能模拟接口和虚拟方法... 那么,如何模拟一个我无法控制的具体类?
这是MyModule
类:
public class MyModule : IHttpModule
{
ILogger _logger;
public long RequestTime { get; private set; }
Stopwatch _stopwatch;
public MyModule(ILogger logger)
{
_logger = logger;
}
public void Init(HttpApplication context)
{
context.BeginRequest += OnBeginRequest;
context.EndRequest += OnEndRequest;
}
public void Dispose() { }
void OnBeginRequest(object sender, EventArgs e)
{
_stopwatch = Stopwatch.StartNew();
}
void OnEndRequest(object sender, EventArgs e)
{
_stopwatch.Stop();
RequestTime = _stopwatch.ElapsedMilliseconds;
}
}
答案 0 :(得分:1)
我认为通常不建议这样做,但是您可以使用Reflection模拟非公共/虚拟成员。
对于当前的implementation,就像这样
public class MockHttpApplication : HttpApplication
{
public void RaiseBeginRequest()
{
FindEvent("EventBeginRequest").DynamicInvoke(this, EventArgs.Empty);
}
public void RaiseEndRequest()
{
FindEvent("EventEndRequest").DynamicInvoke(this, EventArgs.Empty);
}
private Delegate FindEvent(string name)
{
var key = typeof(HttpApplication)
.GetField(name, BindingFlags.Static | BindingFlags.NonPublic)
.GetValue(null);
var events = typeof(HttpApplication)
.GetProperty("Events", BindingFlags.Instance | BindingFlags.NonPublic)
.GetValue(this) as EventHandlerList;
return events[key];
}
}