简体   繁体   English

在HttpContext.Current.Request中模拟ServerVariables

[英]Mock ServerVariables in the HttpContext.Current.Request

One of my services uses server variable provided by IIS through such code 我的一个服务使用IIS通过此类代码提供的服务器变量

var value = System.Web.HttpContext.Current.Request.ServerVariables["MY_CUSTOM_VAR"];

What I've tried is to mock those object and insert my own variable/collection and check few cases(eg variable is missing, value is null ...) I'm able to create instances of HttpContext, HttpRequest, HttpResponse and assign them properly however each of them is just a plain class without interface or virtual properties and initialization of ServerVariables happens somewhere under the hood. 我试过的是模拟那些对象并插入我自己的变量/集合并检查几个案例(例如变量丢失,值为null ...)我能够创建HttpContext,HttpRequest,HttpResponse的实例并分配它们但是它们中的每一个都只是一个没有接口或虚拟属性的普通类,并且ServerVariables的初始化发生在某个地方。

HttpContext mocking: HttpContext嘲笑:

var httpRequest = new HttpRequest("", "http://excaple.com/", "");
var stringWriter = new StringWriter();
var httpResponse = new HttpResponse(stringWriter);
var httpContextMock = new HttpContext(httpRequest, httpResponse);
HttpContext.Current = httpContextMock;

Attempt #1 Call private method via reflection 尝试#1通过反射调用私有方法

var serverVariables = HttpContext.Current.Request.ServerVariables;
var serverVariablesType = serverVariables.GetType();
MethodInfo addStaticMethod = serverVariablesType.GetMethod("AddStatic", BindingFlags.Instance | BindingFlags.FlattenHierarchy | BindingFlags.NonPublic);

addStaticMethod.Invoke(serverVariables, new object[] {"MY_CUSTOM_VAR", "value"});

Failed with error saying that collection is readonly. 失败,错误表明集合是只读的。

Attempt #2 Replace ServerVariables with my own instance 尝试#2用我自己的实例替换ServerVariables

var request = HttpContext.Current.Request;
var requestType = request.GetType();
var variables = requestType.GetField("_serverVariables", BindingFlags.Instance | BindingFlags.NonPublic);

variables.SetValue(request, new NameValueCollection
{
    { "MY_CUSTOM_VAR", "value" }
});

Failed with error that it's not possible to cast NameValueCollection to HttpServerVarsCollection. 失败,因为无法将NameValueCollection强制转换为HttpServerVarsCollection。 That's because HttpServerVarsCollection is actually a internal class so I couldn't either to make an instance of it or cast to it. 那是因为HttpServerVarsCollection实际上是一个内部类,所以我既不能创建它的实例,也不能强制转换它。

So the question is - How can I mock ServerVariables or insert value there? 所以问题是 - 我如何模拟ServerVariables或在那里插入值? Thanks 谢谢

I found the answer here http://forums.asp.net/t/1125149.aspx 我在http://forums.asp.net/t/1125149.aspx找到了答案

It is not possible to access the objects that stores server variables. 无法访问存储服务器变量的对象。 But it can be accessed using reflection. 但它可以使用反射访问。 So using reflection we can set the values that we want. 因此,使用反射我们可以设置我们想要的值。 Following extension method will help set any server variable. 以下扩展方法将帮助设置任何服务器变量。

public static void AddServerVariable(this HttpRequest request, string key, string value)
    {
        BindingFlags temp = BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static;
        MethodInfo addStatic = null;
        MethodInfo makeReadOnly = null;
        MethodInfo makeReadWrite = null;

        Type type = request.ServerVariables.GetType();
        MethodInfo[] methods = type.GetMethods(temp);
        foreach (MethodInfo method in methods)
        {
            switch (method.Name)
            {
                case "MakeReadWrite":
                    makeReadWrite = method;
                    break;
                case "MakeReadOnly":
                    makeReadOnly = method;
                    break;
                case "AddStatic":
                    addStatic = method;
                    break;
            }
        }
        makeReadWrite.Invoke(request.ServerVariables, null);
        string[] values = { key, value };
        addStatic.Invoke(request.ServerVariables, values);
        makeReadOnly.Invoke(request.ServerVariables, null);
    }

The problem is, HttpContext and HttpRequest objects are not mockable. 问题是, HttpContextHttpRequest对象不可模拟。

HttpContext 的HttpContext

This is the vintage asp.net context. 这是复古的asp.net上下文。 The problem with this is that it has no base class and isn't virtual, and hence is unusable for testing (cannot mock it). 这个问题是它没有基类而且不是虚拟的,因此不能用于测试(不能模拟它)。 It's recommended to not pass it around as function arguments, instead pass around variables of type HttpContextBase. 建议不要将它作为函数参数传递,而是传递HttpContextBase类型的变量。

HttpContextBase HttpContextBase

This is the (new to c# 3.5) replacement to HttpContext. 这是(新的c#3.5)替换HttpContext。 Since it is abstract, it is now mockable. 由于它是抽象的,现在它是可以模仿的。 The idea is that your functions that expect to be passed a context should expect to receive one of these. 我们的想法是,期望传递上下文的函数应该会收到其中一个。 It is concretely implemented by HttpContextWrapper 它由HttpContextWrapper具体实现

HttpContextWrapper HttpContextWrapper

Also new in C# 3.5 - this is the concrete implementation of HttpContextBase. 在C#3.5中也是新的 - 这是HttpContextBase的具体实现。 To create one of these in a normal webpage, use new HttpContextWrapper(HttpContext.Current). 要在普通网页中创建其中一个,请使用新的HttpContextWrapper(HttpContext.Current)。

The idea is that to make your code unit-testable, you declare all your variables and function parameters to be of type HttpContextBase, and use an IOC framework eg Castle Windsor to get it injected. 我们的想法是,为了使您的代码单元可测试,您将所有变量和函数参数声明为HttpContextBase类型,并使用IOC框架(例如Castle Windsor)来注入它。 In normal code, castle is to inject the equivalent of 'new HttpContextWrapper(HttpContext.Current)', whereas in test code you're to be given a mock of HttpContextBase. 在普通代码中,castle将注入相当于'new HttpContextWrapper(HttpContext.Current)',而在测试代码中,您将获得一个HttpContextBase的模拟。

(source: http://www.splinter.com.au/httpcontext-vs-httpcontextbase-vs-httpcontext/ ) (来源: http//www.splinter.com.au/httpcontext-vs-httpcontextbase-vs-httpcontext/

You need to wrap it with HttpContextBase like so: 你需要用HttpContextBase包装它,如下所示:

var result = YourMethod(new HttpContextWrapper(HttpContext.Current));

Then when you write your test for YourMethod(HttpContextBase context) , you can easily mock the HttpContextBase using Moq: 然后当你为YourMethod(HttpContextBase context)编写测试时,你可以使用Moq轻松地模拟HttpContextBase

var request = new Mock<HttpRequestBase>(MockBehavior.Strict);
request.Setup(x => x.ServerVariables).Returns(new System.Collections.Specialized.NameValueCollection{
  { "MY_CUSTOM_VAR", "your value here" }
});

var context = new Mock<HttpContextBase>();
context.SetupGet(x => x.Request).Returns(request.Object);

Then your HttpContext object can be called with context.Object ; 然后可以使用context.Object调用您的HttpContext对象;

This is exactly the case where you should refactor your code. 这正是您应该重构代码的情况。

Example of refactored code: 重构代码示例:

public class MyService
{
    public ServiceResult SomeMethod()
    {
        ...
        var value = GetVariable(System.Web.HttpContext.Current, "some var name");
        ...
    }

    protected virtual string GetVariable(HttpContext fromContext, string name)
    {
        return fromContext.Request.ServerVariables[name];
    }

}

In your test: 在你的测试中:

class MyTestableService : MyService
{
    protected override string GetVariable(HttpContext fromContext, string name)
    {
        return MockedVariables[name];
    }

    public Dictionary<string, string> MockedVariables { get; set; }
}

[Test]
public void TestSomeMethod()
{
    var serviceUnderTest = 
           new MyTestableService 
           { 
                MockedVariables = new Dictionary<string, string>
                {
                     { "some var name", "var value" }
                }
           };

     //more arrangements here

     Assert.AreEqual(expectedResult, serviceUnderTest.SomeMethod());
 }

Of course, you can extract the method to a separate dependency altogether. 当然,您可以将方法完全提取到单独的依赖项中。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 生产中的HttpContext.Current.Request为null - HttpContext.Current.Request null in production C#Web API模拟HttpContext.Current.Request测试文件上传 - C# Web API Mock HttpContext.Current.Request to test file upload HttpContext.Request或HttpContext.Current.Request URL中是否缺少“#”字母? - '#' letter is missing in HttpContext.Request or HttpContext.Current.Request url? 我可以使用动态重定向HttpContext.Current.Request吗? - can I use dynamic to redirect HttpContext.Current.Request? HttpControllerContext.Request和HttpContext.Current.Request之间的区别 - Difference between HttpControllerContext.Request and HttpContext.Current.Request HttpContext.Current.Request和Page.Request中的Url.Host - Url.Host in HttpContext.Current.Request and Page.Request 带有Angular UI路由器的HttpContext.Current.Request - HttpContext.Current.Request with Angular ui-router 我想在我的Windows窗体中包含HttpContext.Current.Request - I want to include HttpContext.Current.Request in my windows forms 什么HttpContext.Current.Request [“ CarName”]!= null,它在做什么? - what HttpContext.Current.Request[“CarName”] != null, what it is doing? HttpContext.Current.Request.ServerVariables [“HTTP_REFERER”] null - HttpContext.Current.Request.ServerVariables[“HTTP_REFERER”] null
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM