如何在 IHttpModule 中测试 HttpApplication 事件

2024-04-01

我在写信HttpModule并且需要测试它,我正在使用C#, .NET4.5.2, NUnit and Moq.

我想测试的方法是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 https://msdn.microsoft.com/en-us/library/system.web.httpapplication(v=vs.110).aspx这就是问题开始的地方,...可以创建HttpApplication但是没有办法设置HttpContext因为它是只读的并且无法将其传递(通过构造函数或类似的东西)...

我没有VS2015 Ultimate并且不能使用Microsoft.Fakes (Shims https://msdn.microsoft.com/en-us/library/hh549175(v=vs.110).aspx),而 ATM 是我找到的唯一解决方案是创建一个包装器 https://stackoverflow.com/questions/1182338/unit-testing-an-httpapplication这听起来不像是最直接的解决方案......

当我想到这一点时,我确信有人已经遇到了这个确切的问题(因为每次有人写HttpModule在 TDD 中他需要嘲笑HttpApplication或者做一些解决方法)

一个人如何测试事件IHttpModules?有没有办法模拟 HttpApplication?最好与Moq.

EDIT:这是我正在尝试测试的代码...它是来自的标头重写器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);
        }
    }
}

下面显示了使上述案例可测试的潜在解决方法

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);
        }
    }
}

测试结果会像

[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);
    }

}

苏特的一项观察是,ip决心"System.Byte[]"我认为这不是预期的行为。重新检查proxyv2HeaderStartRequence.

除了添加访问请求的工厂方法外,其余测试代码保持不变。观察实际的实现,请求是如何包装在HttpRequestBase派生类允许将模拟交换到其位置以进行测试。

现在应该允许在模块中应用 TDD。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何在 IHttpModule 中测试 HttpApplication 事件 的相关文章

  • 元组在 VS2012 中如何工作?

    Visual Studio 2012 功能 tuples但不是可变参数模板 这是如何完成的 如何在不使用可变模板的情况下实现元组 简而言之 微软做了与之前在 NET 中实现类似元组的数据类型完全相同的事情 创建许多版本 每个版本都有固定数量
  • C free() 是如何工作的? [复制]

    这个问题在这里已经有答案了 可能的重复 malloc 和 free 如何工作 https stackoverflow com questions 1119134 how malloc and free work include
  • C++ 将联合强制转换为其成员类型之一

    以下对我来说似乎完全符合逻辑 但不是有效的 C 联合不能隐式转换为其成员类型之一 有人知道为什么不这样做的充分理由吗 union u int i char c function f int i int main u v v i 6 f v
  • 带有运算符语法的错误消息,但不带有函数语法的错误消息

    为什么我在调用 unary 时收到错误消息 使用运算符语法 如果我用函数语法调用它就可以了 现场演示 https godbolt org z j7AbeQ template
  • 将日期时间转换为指定格式

    我有这个日期格式yy MM dd HH mm ss ex 12 02 21 10 56 09 问题是 当我尝试使用以下代码将其转换为不同格式时 CDate 12 02 21 10 56 09 ToString MMM dd yyyy HH
  • 静态类与类的实例

    我有一个静态类 用于访问我的公共属性 整个应用程序的全局属性 和我在应用程序运行期间使用的方法 例如 我在静态类中设置了一些属性 并且在应用程序运行时我可以从属性中获取值 但我可以使用单例模式创建非静态类并以相同的方式使用它 问题 对于我的
  • 如何在win32中使用GetSaveFileName保存文件?

    我编写此代码是为了获取 fileName 来保存我的文件 include stdafx h include
  • 在 C# 中何时使用 ArrayList 而不是 array[]?

    我经常使用一个ArrayList而不是 正常 array 当我使用时 我感觉好像我在作弊 或懒惰 ArrayList 什么时候可以使用ArrayList在数组上 数组是强类型的 并且可以很好地用作参数 如果您知道集合的长度并且它是固定的 则
  • 使用 C# 中的 Google 地图 API 和 SSIS 包获取行驶距离

    更新 找到了谷歌距离矩阵并尝试相应地修改我的代码 我在这里收到无效参数错误 return new GeoLocation dstnc uri ToString catch return new GeoLocation 0 0 https 基
  • 无法为 wsdl 文件创建服务引用

    I have wsdl文件和xsd我本地机器上的文件 我想在项目中添加服务引用 我没有网络服务 我只有wsdl file 我收到以下错误 The document was understood but it could not be pro
  • 使用 OleDbCommandBuilder 时访问 SQL 语法错误

    我要在 C 中使用 OleDbDataAdapter 在 Access 数据库中插入数据 但收到错误消息INSERT INTO 命令中的语法错误 BackgroundWorker worker new BackgroundWorker Ol
  • 如何使用 CSI.exe 脚本参数

    当你运行csi exe 安装了 Visual Studio 2015 update 2 您将得到以下语法 Microsoft R Visual C Interactive Compiler version 1 2 0 51106 Copyr
  • 使用多线程进行矩阵乘法?

    我应该使用线程将两个矩阵相乘 有两件事 当我运行程序时 我不断得到 0 我还收到消息错误 对于每个错误 它在粗体行上显示 警告 从不兼容的指针类型传递 printMatrix 的参数1 我尝试打印输出 还要注意 第一个粗体块 这是我解决问题
  • EnumDisplayDevices 与 WMI Win32_DesktopMonitor,如何检测活动监视器?

    对于我当前的 C 项目 我需要为在大量计算机上连接并处于活动状态的每个监视器检测一个唯一的字符串 研究指出了两种选择 使用 WMI 并查询 Win32 DesktopMonitor 以获取所有活动监视器 使用 PNPDeviceID 来唯一
  • 无法在 C# 中为 EventArgs 分配使用派生类型的事件处理程序

    所以我有一个事件声明如下 public event EventHandler OnChangeDetected 然后我有以下处理程序被分配给该事件 myObject OnChangeDetected OnTableChanged 我的理解是
  • 如何在dll级别读取app.config? [复制]

    这个问题在这里已经有答案了 我在一个解决方案中有一个控制台应用程序项目和库项目 dll The 图书馆项目有 app config 文件 我在其中存储我在库中使用的一些键值对 控制台应用程序引用此 dll 我有另一个 app config
  • “必须声明标量变量”错误[重复]

    这个问题在这里已经有答案了 必须声明标量变量 Id SqlConnection con new SqlConnection connectionstring con Open SqlCommand cmd new SqlCommand cm
  • 这种尺寸对齐是如何工作的

    对于所提供的评论 我无法理解以下代码 这段代码的作用是什么 以及等效的代码是什么8 aligned segment size must be 4 aligned attr gt options ssize 3 Here ssize is o
  • C++ Boost ASIO 简单的周期性定时器?

    我想要一个非常简单的周期性计时器每 50 毫秒调用我的代码 我可以创建一个始终休眠 50 毫秒的线程 但这很痛苦 我可以开始研究用于制作计时器的 Linux API 但它不可移植 I d like使用升压 我只是不确定这是否可能 boost
  • 嵌入式二进制资源 - 如何枚举嵌入的图像文件?

    我按照中的说明进行操作这本书 http www apress com book view 9781430225492 关于资源等的章节 我不太明白的是 如何替换它 images Add new BitmapImage new Uri Ima

随机推荐