不知道为什么你已经决定硬连线的依赖关系为新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);
}
不知道什么试图在这里测试。没有太多的行为,它是基础设施。我建议留下您的HttpModule作为基础设施问题。将您认为需要单元测试的任何行为抽象掉到单独的类/服务和单元测试该行为。您可以在其他类型的测试中覆盖HttpModule,例如验收测试。 – Spock
@Raj:感谢您的回复。其实我同意你的看法。在我的“BeginRequest”和“EndRequest”方法的处理程序中,我使用执行主要工作的“LogService”。所以我想在处理程序中测试'LogService'方法被调用。 – user2598794
如何将LogService注入模块? – Spock