2017-02-22 65 views
2

我在寫HttpModule並需要測試它,我使用C#,.NET4.5.2,NUnitMoq如何測試IHttpModules中的HttpApplication事件

方法,我想測試Context_BeginRequest

public class XForwardedForRewriter : IHttpModule 
{ 
    public void Init(HttpApplication context) 
    { 
     context.BeginRequest += Context_BeginRequest; 
    } 

    public void Context_BeginRequest(object sender, EventArgs e) { ... } 
} 

sender這裏是HttpApplication,這就是問題的開始,...一個可以創建HttpApplication實例但是沒有辦法設置HttpContext自它是隻讀的,沒有辦法通過它(通過構造函數或東西一樣)......

我沒有VS2015 Ultimate不能使用Microsoft.FakesShims)和ATM的唯一解決方案此我找到了is to create a wrapper這聽起來不像最直接的解決方案......

當我想到這個時,我確信有人已經遇到了這個確切的問題(因爲每次在TDD中編寫HttpModule他都需要模擬HttpApplication或做一些解決方法)

如何測試一個事件IHttpModules?有沒有一種模擬HttpApplication的方法?優先Moq

編輯:這是我想對代碼進行測試......它的頭重寫器從PROXY v2二進制好老X-Forwarded-For ...

public class XForwardedForRewriter : IHttpModule 
{ 
    public void Dispose() 
    { 
     throw new NotImplementedException(); 
    } 

    byte[] proxyv2HeaderStartRequence = new byte[12] { 0x0D, 0x0A, 0x0D, 0x0A, 0x00, 0x0D, 0x0A, 0x51, 0x55, 0x49, 0x54, 0x0A }; 

    public void Init(HttpApplication context) 
    { 
     context.BeginRequest += Context_BeginRequest; 
    } 

    public void Context_BeginRequest(object sender, EventArgs e) 
    { 
     var request = ((HttpApplication)sender).Context.Request; 

     var proxyv2header = request.BinaryRead(12); 
     if (!proxyv2header.SequenceEqual(proxyv2HeaderStartRequence)) 
     { 
      request.Abort(); 
     } 
     else 
     { 
      var proxyv2IpvType = request.BinaryRead(5).Skip(1).Take(1).Single(); 
      var isIpv4 = new byte[] { 0x11, 0x12 }.Contains(proxyv2IpvType); 
      var ipInBinary = isIpv4 ? request.BinaryRead(12) : request.BinaryRead(36); 
      var ip = Convert.ToString(ipInBinary); 

      var headers = request.Headers; 
      Type hdr = headers.GetType(); 
      PropertyInfo ro = hdr.GetProperty("IsReadOnly", 
       BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.IgnoreCase | BindingFlags.FlattenHierarchy); 

      ro.SetValue(headers, false, null); 

      hdr.InvokeMember("InvalidateCachedArrays", 
       BindingFlags.InvokeMethod | BindingFlags.NonPublic | BindingFlags.Instance, 
       null, headers, null); 

      hdr.InvokeMember("BaseAdd", 
       BindingFlags.InvokeMethod | BindingFlags.NonPublic | BindingFlags.Instance, 
       null, headers, 
       new object[] { "X-Forwarded-For", new ArrayList { ip } }); 

      ro.SetValue(headers, true, null); 
     } 
    } 
} 
+0

你到底想要測試什麼?顯示SUT,也許可以找到解決辦法。 – Nkosi

回答

2

下面顯示了圍繞一個潛在的工作使上述情況下的測試,能夠在SUT的

[TestClass] 
public class XForwardedForRewriterTests { 

    [TestMethod] 
    public void Request_Should_Abort() { 
     //Arrange 
     var request = Mock.Of<HttpRequestBase>(); 

     var sut = new XForwardedForRewriter(); 
     //replace with mock request for test 
     sut.GetRequest = (object sender) => request; 

     //Act 
     sut.Context_BeginRequest(new object(), EventArgs.Empty); 

     //Assert 
     var mockRequest = Mock.Get(request); 
     mockRequest.Verify(m => m.Abort(), Times.AtLeastOnce); 
    } 


    [TestMethod] 
    public void Request_Should_Forward() { 
     //Arrange 
     var request = Mock.Of<HttpRequestBase>(); 

     var mockRequest = Mock.Get(request); 
     //setup mocked request with desired behavior for test 
     var proxyv2HeaderStartRequence = new byte[12] { 0x0D, 0x0A, 0x0D, 0x0A, 0x00, 0x0D, 0x0A, 0x51, 0x55, 0x49, 0x54, 0x0A }; 
     mockRequest 
      .Setup(m => m.BinaryRead(12)) 
      .Returns(proxyv2HeaderStartRequence); 

     var fakeProxyv2IpvType = new byte[5] { 0x00, 0x12, 0x00, 0x00, 0x00 }; 
     mockRequest 
      .Setup(m => m.BinaryRead(5)) 
      .Returns(fakeProxyv2IpvType); 

     var headers = new NameValueCollection(); 
     mockRequest.Setup(m => m.Headers).Returns(headers); 

     var sut = new XForwardedForRewriter(); 
     //replace with mock request for test 
     sut.GetRequest = (object sender) => request; 

     //Act 
     sut.Context_BeginRequest(new object(), EventArgs.Empty); 

     //Assert 
     //...check request headers 
     var xForwardedFor = headers["X-Forwarded-For"]; 
     Assert.IsNotNull(xForwardedFor); 
    } 

} 

public class XForwardedForRewriter : IHttpModule { 
    public void Dispose() { 
     throw new NotImplementedException(); 
    } 

    byte[] proxyv2HeaderStartRequence = new byte[12] { 0x0D, 0x0A, 0x0D, 0x0A, 0x00, 0x0D, 0x0A, 0x51, 0x55, 0x49, 0x54, 0x0A }; 

    public void Init(HttpApplication context) { 
     context.BeginRequest += Context_BeginRequest; 
    } 

    public Func<object, HttpRequestBase> GetRequest = (object sender) => { 
     return new HttpRequestWrapper(((HttpApplication)sender).Context.Request); 
    }; 

    public void Context_BeginRequest(object sender, EventArgs e) { 
     var request = GetRequest(sender); 

     var proxyv2header = request.BinaryRead(12); 
     if (!proxyv2header.SequenceEqual(proxyv2HeaderStartRequence)) { 
      request.Abort(); 
     } else { 
      var proxyv2IpvType = request.BinaryRead(5).Skip(1).Take(1).Single(); 
      var isIpv4 = new byte[] { 0x11, 0x12 }.Contains(proxyv2IpvType); 
      var ipInBinary = isIpv4 ? request.BinaryRead(12) : request.BinaryRead(36); 
      var ip = Convert.ToString(ipInBinary); 

      var headers = request.Headers; 
      var hdr = headers.GetType(); 
      var ro = hdr.GetProperty("IsReadOnly", 
       BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.IgnoreCase | BindingFlags.FlattenHierarchy); 

      ro.SetValue(headers, false, null); 

      hdr.InvokeMember("InvalidateCachedArrays", 
       BindingFlags.InvokeMethod | BindingFlags.NonPublic | BindingFlags.Instance, 
       null, headers, null); 

      hdr.InvokeMember("BaseAdd", 
       BindingFlags.InvokeMethod | BindingFlags.NonPublic | BindingFlags.Instance, 
       null, headers, 
       new object[] { "X-Forwarded-For", new ArrayList { ip } }); 

      ro.SetValue(headers, true, null); 
     } 
    } 
} 

一個觀察是ip解析爲"System.Byte[]"我相信這是不期望的行爲。重新檢查proxyv2HeaderStartRequence

除了添加Factory方法來訪問請求外,其餘被測代碼保持不變。注意實際的實現,請求如何包裝在HttpRequestBase派生類中,該派生類允許在它的位置交換模擬代碼進行測試。

現在應該允許TDD與模塊一起使用。

+1

嗨Nkosi,這確實有用,我必須說很聰明。謝謝 –