在HttpContext.Current.Request中模拟ServerVariables

And*_*ren 7 c# reflection unit-testing httpcontext

我的一个服务使用IIS通过此类代码提供的服务器变量

var value = System.Web.HttpContext.Current.Request.ServerVariables["MY_CUSTOM_VAR"];
Run Code Online (Sandbox Code Playgroud)

我试过的是模拟那些对象并插入我自己的变量/集合并检查几个案例(例如变量丢失,值为null ...)我能够创建HttpContext,HttpRequest,HttpResponse的实例并分配它们但是它们中的每一个都只是一个没有接口或虚拟属性的普通类,并且ServerVariables的初始化发生在某个地方.

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;
Run Code Online (Sandbox Code Playgroud)

尝试#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"});
Run Code Online (Sandbox Code Playgroud)

失败,错误表明集合是只读的.

尝试#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" }
});
Run Code Online (Sandbox Code Playgroud)

失败,因为无法将NameValueCollection强制转换为HttpServerVarsCollection.那是因为HttpServerVarsCollection实际上是一个内部类,所以我不能创建它的实例或强制转换它.

所以问题是 - 我如何模拟ServerVariables或在那里插入值?谢谢

Bir*_*rey 6

我在http://forums.asp.net/t/1125149.aspx找到了答案

无法访问存储服务器变量的对象.但它可以使用反射访问.因此,使用反射我们可以设置我们想要的值.以下扩展方法将帮助设置任何服务器变量.

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);
    }
Run Code Online (Sandbox Code Playgroud)


gal*_*nus 1

这正是您应该重构代码的情况。

重构代码示例:

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

}
Run Code Online (Sandbox Code Playgroud)

在你的测试中:

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());
 }
Run Code Online (Sandbox Code Playgroud)

当然,您可以将该方法完全提取到单独的依赖项中。