我有一个简单的 Http 模块:
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() 方法,如果可以将这些依赖项外部化以便注入(inject) stub /模拟版本等,就会变得容易得多。
此外,您的问题并未说明您使用的是 IOC container .你可以register the HttpModule with the container并在运行时提供外部依赖。您的问题也没有说明使用 isoloation/mock object framework . 因此,我将为您提供一个解决方案,您可以使用手写 stub 和模拟来验证是否调用了 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 因此我可以提供一个 Mocked verion 和一个委托(delegate) Func 允许我 stub 新的 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,这样您就可以验证交互,即是否调用了 LogInfo() 方法等。您还需要一个 stub 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 非常重要。它不是真正的记录器服务,而是模拟版本。当我们执行 public class MockLoggerService : ILogService 时,这意味着我们正在为记录器服务提供另一层间接访问,以便我们可以验证行为的交互。
您还注意到我提供了一个 bool 变量来验证是否调用了 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);
}
关于unit-testing - 如何在 HttpModule 中模拟事件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19676248/