繁体   English   中英

在单元测试中设置HttpContext.Current.Application键

[英]Setting HttpContext.Current.Application key in unit tests

我有一个方法,它在内部使用静态HttpContext.Current.Application来存储一个键值对,然后在整个应用程序中检索它。

现在,我想做的是对该方法进行单元测试。 我现在拥有的是:

private const string Foo = "foobar";

[TestInitialize]
public void InitializeContext()
{
    HttpContext.Current = new HttpContext(new HttpRequest(null, "http://tempuri.org", null), new HttpResponse(null));
    HttpContext.Current.Application["fooKey"] = Foo;
}

[TestCleanup]
public void DisposeContext()
{
    HttpContext.Current = null;
}

一切都很好,但是当我尝试在TestMethod该值时,该值为null。

var fooValue = (string)HttpContext.Current.Application["fooKey"];

显然,我正在测试的方法中的值为null。

到目前为止我尝试的是嘲笑背景 - 没有效果。 然后我在SO上看到了伪造的HTTP上下文解决方案,仍然无效。 我甚至尝试接受HttpApplicationStateBase / HttpApplicationStateWrapper并完成它们,但它变得一团糟,我觉得有比这更简单的解决方案。

我最终做的是使用这个惊人的库。 对于那些不知道为什么没有用的人, 这个 StackOverflow问题解释了一个类似的问题(奇怪的是我在问之前找不到它)。

根据Jeff Sternal的回答:

HttpContext.Application由您无法正常访问的私有单例提供。

你可以通过反思设置这个,但我个人甚至都不会打扰。

相反,您应该将可测试代码与HttpContext.Current之类的全局状态隔离开:只需将您要查找的值传递给您要测试的方法。

您可以清楚地看到问题在Reflector中(或者如果您已经下载了.NET代码中的源代码):HttpContext.Application get访问器每次调用它时都会返回一个新的HttpApplicationState实例,除非有什么东西(比如ASP)。 NET框架)设置HttpApplicationFactory._theApplicationFactory._state。

最终,我最终做了类似的事情:

using (HttpSimulator simulator = new HttpSimulator())
{
    simulator.SimulateRequest(new Uri("http://localhost/"));
    HttpContext.Current.Application.Add("fooKey", Foo);
    this.httpContext = HttpContext.Current;
}

然后将保存的引用传递给我的方法。

更新#1 :虽然在编写使用HttpContext某些方面的测试时,我的原始答案可能很有用,但使用HttpContext.Application属性并不容易,因为该属性依赖于HttpApplicationFactory类的static internal属性。

一种可能的解决方案是不依赖于测试方法中的Application属性,而是将值传递给方法。 例如:

cls.MethodUnderTest("foo value");

这意味着您可以更改测试类,使其不依赖于HttpContext.Application (假设没有其他测试需要它),并且您的测试类将只包含测试:

[TestMethod]
public void Test1()
{
    // This would be the value you previously stored in the Application property
    object fooValue = <some value>;
    YourClassName cls = new YourClassName();
    cls.MethodUnderTest(fooValue);
}

然后,如果您有其他测试用不同的值测试方法,您可以简单地创建一个测试并传入该值,而不是必须添加以首先将其添加到HttpContext.Application

作为旁注......在输入时我已经意识到我可能用我的单元测试和cookie做同样的事情!

原始答案

我目前正在尝试类似的事情,但使用cookies。 我所做的是将HttpContext.Current传递给类,存储引用,然后在类方法中使用它。

所以:

public class MyClass
{
    private HttpContext _httpContext;

    // **** The caller would supply HttpContext.Current here ****
    public MyClass(HttpContext httpContext) {               
        _httpContext = httpContext;
    }

    public void SomeMethod() {
        ...
        //  Do something here with _httpContext
        ...
    }
}

然后说到单元测试,我会做类似于你现在所做的事情:

public void Test1()
{
    HttpRequest request = new HttpRequest(null, "http://tempuri.org", null);
    HttpResponse response = new HttpResponse(null);
    HttpContext httpContext = new HttpContext(request, response);

    httpContext.Request.Cookies.Add(new HttpCookie("cookieName"));

    MyClass cls = new MyClass(httpContext);
    cls.SomeMethod();
}

根据您的情况,您可以更改引用HttpContext.Current的类以接受HttpContext实例并将HttpContext.Current传递给它,就像上面的示例一样。 然后,不是在TestInitialize中设置HttpContext.CurrentTestInitialize创建一个成员变量并使用它而不是依赖于静态Current属性。

例如:

private const string Foo = "foobar";
private HttpContext _httpContext = null;

[TestInitialize]
public void InitializeContext()
{
    _httpContext = new HttpContext(new HttpRequest(
        null, "http://tempuri.org", null), new HttpResponse(null));
    _httpContext.Application["fooKey"] = Foo;
}

[TestMethod]
public void Test1()     <-- **** Example Test Method ****
{
    YourClassName cls = new YourClassName(_httpContext);
    cls.MethodUnderTest();
}

[TestCleanup]
public void DisposeContext()
{
    _httpContext = null;
}

我没有一个可行的例子,所以我现在只是关闭记忆,但希望它会提供一些帮助。

为什么不尝试在接口中实现它并模拟该接口。 然后,您可以使用依赖注入框架将其注入控制器

public interface IApplicationContext
{
    string FooKey { get; set; }
}

public class ApplicationContext : IApplicationContext
{
    public string FooKey
    {
        return HttpContext.Current.Application["fooKey"];
    }
}

public class YourTests
{
    [TestInitialize]
    public void InitializeContext()
    {
        // From MOQ mocking framework. https://github.com/moq/moq4
        Mock<IApplicationContext> applicationMock = new Mock<IApplicationContext>();
        applicationMock.SetupGet(x => x.FooKey).Returns("foo");
    }
}

public class YourController : Controller
{
    private IApplicationContext applicationContext;

    // Inject the application context using a dependency injection framework or manually in the constructor.
    public YourController (IApplicationContext applicationContext)
    {
        this.applicationContext = applicationContext;
        var foo = applicationContext.FooKey;
    }
}

暂无
暂无

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

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM