Scott
Scott

Reputation: 13921

Unit testing code that relies on a SessionManager class with a dependency on HttpContext.Current

I have a SessionManager class in an existing application that looks like this:

public class SessionManagerBase<TKey>
{
    public static void AddItem(TKey key, object item)
    {
        _httpContext.Session[key.ToString()] = item;
    }

    public static T GetItem<T>(TKey key)
    {
        object item = _httpContext.Session[key.ToString()];
        return item == null ? default(T) : (T) Convert.ChangeType(item, typeof (T));
    }

    // etc...

    private static HttpContextBase _httpContext
    {
        get
        {
            return new HttpContextWrapper(HttpContext.Current);
        }
    }
}

In my HomeController, I have code like the following:

public ActionResult Landing(string id)
{
    SessionManager.GetItem<Campaign>(SessionKeys.Campaign)

    // commented for brevity

    return View("Index");
}

When I run a unit test on the Landing method, the test fails because HttpContext.Current is null. I've mocked the Session object in my unit tests, and if I attempt to access Session directly in the Landing method (i.e. Session["SomeValue"]) it works, but any code that relies on the SessionManager is broken.

Bottom line is that I want a class I can use to access Session values in a generic, strongly typed manner, but that can also be unit tested. Anyone have any suggestions on how I can modify this code to achieve that?

Upvotes: 0

Views: 762

Answers (3)

Thierry
Thierry

Reputation: 1030

You could possibly set HttpContext.Current to your _httpContext

Upvotes: 0

aiodintsov
aiodintsov

Reputation: 2605

You can only make it testable by introducing another abstraction layer. That is your own IOurHttpContext and OurDotNetHttpContext : IOurHttpContext for real app and OurTestHttpContext : IOutHttpContext for tests (the latter not needed with moq). OurDotNetHttpContext may look like:

public interface IOutHttpContext {
    object GetSessionVar( string key );
    void SetSessionVar( string key, object value );
}
public class OurDotNetHttpContext : IOutHttpContext {
    public object GetSessionVar(string key) { return HttpContext.Current.Session[key]; }
    public void SetSessionVar(string key, object value) { HttpContext.Current.Session[key] = value; }
}

pretty easy. It may be mo sophisticated though.

Upvotes: 0

SLaks
SLaks

Reputation: 887365

If you've already mocked the Session in your test's HttpContextBase, all you need to do is change SessionManager to accept your custom HttpContextBase (eg, in a [ThreadStatic] field)

Alternatively, make the SessionManager class itself non-static, and make it take an HttpContextBase as a constructor parameter.

In general, you should never use HttpContext.Current. if you want your code to be testable or reusable.

Upvotes: 1

Related Questions