Search code examples
c#asp.net-mvcunit-testinghttpcontext

How to handle httpcontext in UnitTest MVC


I am getting the following error when running unit test: NullReferenceException was unhandled by usercode. Object reference was not set to an instance of an object.

Not sure where I am going wrong. Please advice.

My SessionManager.cs class

public static int customerID
{
    get
    {
        if (HttpContext.Current.Session != null && 
            HttpContext.Current.Session[_customerID] != null)
        {
            return Convert.ToInt32(HttpContext.Current.Session[_customerID]);
        }
        else
        {
            throw new Ramsell.ExceptionManagement.RamsellException();
        }
    }
    set
    {
        if (HttpContext.Current.Session == null) return;
        HttpContext.Current.Session[_customerID] = value;

    }
}

ParticipantController

public ActionResult Index()
{
     int custId = Convert.ToInt32(SessionManager.customerID);
     //code logic about participant lists
     return View();
}

Test method :

 [TestMethod]
    public void IndexTest()
    {
        ParticipantController pCTarget = new ParticipantController();
        const int page = 1;
        const string sortBy = "FirstName";
        const bool ascending = true;
        const string partname = ""; 
        const int success = -1;
        const string id = "";
        var expected = typeof(ParticipantListViewModel);
        ViewResult result = pCTarget.Index(page, sortBy, ascending, partname, success, id) as ViewResult;
        Assert.AreEqual(expected, result.Model.GetType());

    }

//This is my mock class

 public Mock<RequestContext> RoutingRequestContext { get; private set; }
 public Mock<HttpContextBase> Http { get; private set; }
    public Mock<HttpServerUtilityBase> Server { get; private set; }
    public Mock<HttpResponseBase> Response { get; private set; }
    public Mock<HttpRequestBase> Request { get; private set; }
    public Mock<HttpSessionStateBase> Session { get; private set; }
    public Mock<ActionExecutingContext> ActionExecuting { get; private set; }
    public HttpCookieCollection Cookies { get; private set; }

    public MockContext()
    {
        this.RoutingRequestContext = new Mock<RequestContext>(MockBehavior.Loose);
        this.ActionExecuting = new Mock<ActionExecutingContext>(MockBehavior.Loose);
        this.Http = new Mock<HttpContextBase>(MockBehavior.Loose);
        this.Server = new Mock<HttpServerUtilityBase>(MockBehavior.Loose);
        this.Response = new Mock<HttpResponseBase>(MockBehavior.Loose);
        this.Request = new Mock<HttpRequestBase>(MockBehavior.Loose);
        this.Session = new Mock<HttpSessionStateBase>(MockBehavior.Loose);
        this.Cookies = new HttpCookieCollection();

        this.RoutingRequestContext.SetupGet(c => c.HttpContext).Returns(this.Http.Object);
        this.ActionExecuting.SetupGet(c => c.HttpContext).Returns(this.Http.Object);
        this.Http.SetupGet(c => c.Request).Returns(this.Request.Object);
        this.Http.SetupGet(c => c.Response).Returns(this.Response.Object);
        this.Http.SetupGet(c => c.Server).Returns(this.Server.Object);
        this.Http.SetupGet(c => c.Session).Returns(this.Session.Object);
        this.Request.Setup(c => c.Cookies).Returns(Cookies);

        var sessionContainer = new HttpSessionStateContainer("userID", new SessionStateItemCollection(),
                                                new HttpStaticObjectsCollection(), 1, true,
                                                HttpCookieMode.AutoDetect,
                                                SessionStateMode.InProc, false);

        this.Session.Setup(c => c.Add("AspSession", typeof(HttpSessionState).GetConstructor(
                                    BindingFlags.NonPublic | BindingFlags.Instance,
                                    null, CallingConventions.Standard,
                                    new[] { typeof(HttpSessionStateContainer) },
                                    null)
                            .Invoke(new object[] { sessionContainer })));
    }

Solution

  • When you are writing unit tests for controller, you should test it in isolation - i.e. controller's dependencies should be mocked. So, make sure that your controller depends on abstraction rather than concrete SessionManager implementation (make your class non-static and extract ISessionManager interface from your manager class):

    public interface ISessionManager
    {
       int CustomerID { get; set; }
    } 
    
    public class HttpSessionManager : ISessionManager
    {
        // your current implementation goes here
    }
    

    Then mock this abstraction and inject it to controller (sample with Moq):

    [TestMethod]
    public void IndexTest()
    {
        var sessionManager = new Mock<ISessionManager>(); // Mock dependency
        sessionManager.Setup(m => m.CustomerID).Returns(42);
    
        const int page = 1;
        const string sortBy = "FirstName";
        const bool ascending = true;
        const string partname = ""; 
        const int success = -1;
        const string id = "";
        var expectedModelType = typeof(ParticipantListViewModel);
        ParticipantController pCTarget = 
             new ParticipantController(sessionManager.Object); // Inject dependency
    
        var view = pCTarget.Index(page, sortBy, ascending, partname, success, id)
                   as ViewResult;
    
        sessionManager.VerifyGet(m => m.CustomerID); // Verify dependency was called
        Assert.AreEqual(expectedModelType, view.Model.GetType());
    }
    

    And here is controller code:

    public class ParticipantController : Controller
    {
        private readonly ISessionManager _sessionManager;
    
        public ParticipantController(ISessionManager sessionManager) 
        {
            _sessionManager = sessionManager;
        }
    
        public ActionResult Index()
        {
            int custId = _sessionManager.CustomerID;
            //code logic about participant lists
            return View();
        }
    } 
    

    Note that goal of unit-testing is to make you sure that every small part of code is working as expected. If you will depend on other units in test, then test can fail for two reasons - your unit under test behaves not as expected, or there is something wrong with dependency You already hit that case - controller test failed because session manager throws exception. Tests says that controller behaves not as expected. Is it true? No - it's session manager behaves not as expected.