Arvind Bhatt
Arvind Bhatt

Reputation: 157

how to unit test for session variable in controller in mvc

I am unit-testing my controller.

In one of my controller methods I am setting Session variables:

public void Index()
{       Session["foo"] = "bar";
        return View();
}

How can I unit-test this? The problem is that the Session property is null when testing. Injecting is not possible because the Session property is readonly.

I don't want to use any third-party tool or mocking.

Upvotes: 3

Views: 4878

Answers (3)

InsI
InsI

Reputation: 177

I agree with @rouen. do not directly use Session["foo"]. But I think having ValueProvider ans might not be a practical solution, as we only store very few variables, and these values may be and most likely not ur full model. So my approach is something similar to what Vic Smith suggests but a much more IOC (and Mock) friendly.

I would create a provider (i.e a service) to retrieve the session variables

public class SessionVariableProvider : ISessionVariableProvider
    {
        public object GetSessionValue(string key)
        {
            if (!HttpContext.Current.Session.IsNewSession 
                    && HttpContext.Current.Session[key] != null)
                {
                    return HttpContext.Current.Session[key];
                }
                throw new ArgumentNullException(key);
        }
        public void SetSessionValue(string key, object value)
        {
            HttpContext.Current.Session[key] = value;
        }
    }
    public interface ISessionVariableProvider
    {
        object GetSessionValue(string key);
        void SetSessionValue(string key, object value);
    }

Modify your Controller expect ISessionVariableProvider as a parameter.

public class TestController: Controller
    {
        protected readonly ISessionVariableProvider _sessionVariableProvider;

        protected InowiaControllerBase(ISessionVariableProvider sessionVariableProvider)
        {
            Guard.ArgumentNotNull(sessionVariableProvider, "sessionVariableProvider");

            this._sessionVariableProvider = sessionVariableProvider;
        }
        public ActionResult Index()
        {       
            _sessionVariableProvider.SetSessionValue("foo", "bar");
            var foo2 = (string)_sessionVariableProvider.GetSessionValue("foo2");
            return View();
        }
    }

when testing create your own test implementation of ISessionVariableProvider and pass it to the controller.

Upvotes: 0

rouen
rouen

Reputation: 5124

Simply dont use things like Session["foo"] in your controller methods. Best practice is keep action methods unaware of any context-like global objects. Everything your action method needs should be given to her in form of arguments. Note that built-in mechanism of model binding works exactly like that - you dont use Request.Form[], you let "somebody behind the scene" pass it to your action as argument.

Now for the session you can do the same - write you very simple ValueProvider which will know how to recognize arguments you want to fill from session, and you are done. In production your actions will work with session, in test you cant simply pass them any values you want as arguments.

For inspiration look at this http://www.prideparrot.com/blog/archive/2012/7/how_to_create_a_custom_session_value_provider

Upvotes: 6

Vic Smith
Vic Smith

Reputation: 3497

Injecting is not possible because the Session property is readonly.

This means you cannot use setter injection, but could you use constructor injection, ie add a constructor for your controller that is something like:

MyController(Session session)
{
    m_session = session;

    // then call your main constructor
}

Session getSession()
{
    return m_session;
}

You can then use this separate constructor during testing.

Upvotes: 1

Related Questions