簡體   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