UrlRewriteModule
之前都是在测试Http Handler,不过Http Module的测试也较为类似。其原则是相同的:把所有逻辑转发给针对抽象的方法。我们这次就以最最经典的URL重写功能为例,如下:
public interface IUrlRewriteSource
{
string GetRewritePath(string rawUrl);
}
public class UrlRewriteModule : IHttpModule
{
public void Dispose() { }
public UrlRewriteModule()
: this(new RegexUrlRewriteSource(...))
{ }
internal UrlRewriteModule(IUrlRewriteSource source)
{
this.m_source = source;
}
private IUrlRewriteSource m_source;
public void Init(HttpApplication httpApp)
{
httpApp.BeginRequest += (sender, e) =>
{
HttpContext context = ((HttpApplication)sender).Context;
this.TryRewritePath(new HttpContextWrapper(context));
};
}
internal void TryRewritePath(HttpContextBase context)
{
string newUrl = this.m_source.GetRewritePath(context.Request.RawUrl);
if (!String.IsNullOrEmpty(newUrl))
{
context.RewritePath(newUrl);
}
}
}
由于测试需要,我们提取出一个IUrlRewriteSource接口。ASP.NET本身会通过无参数的构造函数进行创建,这时就会使用默认的RegexUrlRewriteSource对象。而在测试的时候,就要创建Mock对象并通过构造函数的重载进行“依赖注入”了。在Init方法中我们直接使用匿名委托来作为BeginRequest事件的处理函数,而其中就把逻辑直接委托给TryRewritePath方法了。TryRewritePath方法会判断Source中得知是否需要进行URL重写,并且在需要的时候调用RewritePath方法。它的测试如下:
[TestMethod]
public void TryRewritePathTest_No_Rewrite()
{
Mock<IUrlRewriteSource> mockSource = new Mock<IUrlRewriteSource>();
mockSource.Setup(s => s.GetRewritePath(It.IsAny<string>()))
.Returns<string>(null).Verifiable();
Mock<HttpContextBase> mockContext = new Mock<HttpContextBase>(MockBehavior.Strict);
mockContext.Setup(c => c.Request.RawUrl).Returns("Hello");
mockContext.Setup(c => c.RewritePath(It.IsAny<string>()))
.Throws(new InvalidOperationException("Should not call the RewritePath method."));
UrlRewriteModule module = new UrlRewriteModule(mockSource.Object);
module.TryRewritePath(mockContext.Object);
mockSource.Verify();
}
[TestMethod]
public void TryRewritePathTest_Rewrite_Article_Detail_Page()
{
string rawUrl = "Article/5";
string targetUrl = "~/Article.aspx?id=5";
Mock<IUrlRewriteSource> mockSource = new Mock<IUrlRewriteSource>();
mockSource.Setup(s => s.GetRewritePath(It.IsAny<string>())).Throws(
new InvalidOperationException("Why so many unnecessary method calls?"));
mockSource.Setup(s => s.GetRewritePath(rawUrl)).Returns(targetUrl).Verifiable();
Mock<HttpContextBase> mockContext = new Mock<HttpContextBase>(MockBehavior.Strict);
mockContext.Setup(c => c.Request.RawUrl).Returns(rawUrl);
mockContext.Setup(c => c.RewritePath(targetUrl)).Verifiable();
UrlRewriteModule module = new UrlRewriteModule(mockSource.Object);
module.TryRewritePath(mockContext.Object);
mockSource.Verify();
mockContext.Verify();
}
在不需要重写的情况下,IUrlRewriteSource对象的GetRewritePath方法永远返回null,而此时也不应该调用HttpContext的RewritePath方法。否则,便判断给出合适的RawUrl和重写目标,并判断RewritePath方法有没有正确调用过便是。其实单元测试就这么简单。