5

I have a method which internally uses the static HttpContext.Current.Application to store a key-value pair and retrieve it throughout the application afterwards.

Now, what I want to do is to unit test that method. What I currently have is this:

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

All good, but when I try to cast that value in the TestMethod, the value is null.

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

Obviously, the value is null in the method which I'm testing as well.

What I tried so far is to mock the context - didn't work as well. Then I saw the fake HTTP context solution on SO, still doesn't work. I even tried accepting HttpApplicationStateBase/ HttpApplicationStateWrapper and working through them, but it becomes a mess and I feel that there's a lot simpler solution than that.

arnaudoff
  • 686
  • 8
  • 20

3 Answers3

3

What I ended up doing is using this amazing library. For those who wonder why nothing worked, this StackOverflow question explains a similar problem (strange how I couldn't find it before asking).

According to Jeff Sternal's answer:

HttpContext.Application is supplied by a private singleton that you can't access normally.

You may be able to set this via reflection, but I personally wouldn't even bother.

Instead you should isolate your testable code from global state like HttpContext.Current: just pass the value you're looking for into the method you want to test.

You can see the problem clearly looking at the code in Reflector (or in the .NET source if you've downloaded that): the HttpContext.Application get accessor returns a new HttpApplicationState instance every time you call it unless something (like the ASP.NET framework) sets HttpApplicationFactory._theApplicationFactory._state.

Eventually, I ended up doing something like:

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

And then passed the saved reference to my method.

Community
  • 1
  • 1
arnaudoff
  • 686
  • 8
  • 20
1

UPDATE #1: While my original answer may be useful when writing tests that use certain aspects of HttpContext it is not that easy to use it with the HttpContext.Application property as that property relies on a static internal property of the HttpApplicationFactory class.

A possible solution would be not to rely on the Application property in the method under test, but instead pass the value into the method. For example:

cls.MethodUnderTest("foo value");

This then means you can change your test class so it has no reliance on HttpContext.Application (assuming no other tests need it) and your test class will simply contain the test:

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

Then if you have other tests that test the method with different values you can simply create a test and pass in that value rather than having to add to add it to HttpContext.Application first.

As a side note....while typing this I have realised that I could probably do the same thing with my unit tests and cookies!

Original Answer

I am currently attempting a similar thing, but with cookies. What I do is pass HttpContext.Current into the class, store a reference, and then use that within the class methods.

So:

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
        ...
    }
}

Then when it comes to unit testing I would do something similar to what you have at the moment like this:

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

For your situation you could change your class that references HttpContext.Current to accept a HttpContext instance and pass HttpContext.Current into it like the example above. Then, instead of setting up HttpContext.Current in TestInitialize you would create a member variable and use that instead of relying of the static Current property.

For example:

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

I don't have a working example to hand, so I'm just going off memory at the moment, but hopefully it will provide some help.

MotoSV
  • 2,348
  • 17
  • 27
  • 1
    I tried the exact same approach. Still doesn't work. I have the feeling that the application property relies on something else internally and it is not set in the unit test. The key is being added, no exceptions or anything, but retrieving it results in a `null` in `MyClass`. – arnaudoff Feb 21 '16 at 01:02
0

Why not try implementing this in an interface and mocking that interface. You could then inject this into your controllers using a dependency injection framework

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;
    }
}
ACOMIT001
  • 510
  • 1
  • 7
  • 21