Mocking the session object with Moq inside ASP.NET MVC and having a clean builder method for session values in the controller

I am currently deep into TDD with ASP.NET MVC and Moq as the mocking framework I have chosen.  I have been pondering on some valid methods in order to integrate both page index and also page size into the request. Page index and page size will be unique to each action for the purposes of this blog post and example.  I wanted to have the page index in the url, but the actual page size stored in session.  I have seen in some demonstration apps that the page size has been made constant inside the actual action and unfortunately I feel this is no good as a nice UI feature is being able to see more items per page of a list of items. 

So in the session I have put it and so I wanted to test this out and check that I am infact getting the expected results.  First off I made a simple contract for the paged data called IPagedModel and also an implementing base class called PagedModel:

    public interface IPagedModel
    {
        int TotalCount { get; }
        int TotalPages { get; }
        int PageIndex { get; }
        int PageSize { get; }
    }
    public class PagedModel : IPagedModel
    {
        private int _totalCount;
        private int _pageIndex;
        private int _pageSize;

        public PagedModel(int pageIndex, int pageSize, int totalCount)
        {
            _totalCount = totalCount;
            _pageIndex = pageIndex;
            _pageSize = pageSize;
        }

        #region IPagedModel Members

        public int TotalCount
        {
            get { return _totalCount; }
        }

        public int TotalPages
        {
            get { return (int)Math.Ceiling((decimal)_totalCount / (decimal)_pageSize); }
        }

        public int PageIndex
        {
            get { return _pageIndex; }
        }

        public int PageSize
        {
            get { return _pageSize; }
        }

        #endregion
    }

And the name of the test is this:

[TestMethod]
public void IndexAction_For_Space_1_Page_2_PageSize_2_Should_Have_PageIndex_1_PageSize_2_TotalCount_9()

I want to submit the required page number to the controller using a base 1 index and then using it inside the controller action using a zero based index. The method for the Index action which I will be testing expects an id for the object which it will be targeting but also a page number which is a nullable type in case it is not found inside the url and so will default to 0.  It is in this action where I am using the Session object to get the desired page size for the action. 

        public ActionResult Index(int id, int? pageIndex)
        {
            if (pageIndex == null)
                pageIndex = 0;

            if (pageIndex > 0)
                pageIndex = pageIndex - 1;

            int pageSize = Session["SpaceController!Index!PageSize"] == null ? 10 : Convert.ToInt32(Session["SpaceController!Index!PageSize"]);

            var space = _repository.GetSpace(id);
            if (space == null)
                return View("NotFound");

            long count;

            var forums = _repository.Get(space, pageIndex ?? 0, pageSize, out count);

            return View(new SpaceIndexViewModel(forums, pageIndex ?? 0, pageSize, (int)count));
        }

First thing is to check if the pageIndex is null and if so make it 0.  Next we want to make the conversion from a base 1 index to a base zero index which we will then use against the repository‚Äôs paging method, so basically if the page index is greater than 0, we want to minus 1 from it and use that.  We then check the session object for a valid pageSize for the action, specific to the controller.  From there on in I perform repository specific code and send the model onto the view.  To test this I want to be able to mock the controller context but also the session state and also I want to control the value return when a get on the session value "SpaceController!Index!PageSize" is made.  From looking at the Nerd Dinner application I have made a controller method to return me an instance on a controller, a test repository together with pre made test data I know and expect.  I have extended the method slightly so that I can supply both a username to indicate the HttpContext is authenticated but also session name value pairs.  I have used the Pair object for this but the NameValuePair would probably have been better.

        SpaceController CreateSpaceControllerAs(string userName, List<Pair> sessionValues)
        {
            var mock = new Mock<ControllerContext>();
            var mockSession = new Mock<HttpSessionStateBase>();
            mock.Setup(cts => cts.HttpContext.Session).Returns(mockSession.Object);
            mock.SetupGet(p => p.HttpContext.User.Identity.Name).Returns(userName);
            mock.SetupGet(p => p.HttpContext.Request.IsAuthenticated).Returns(true);

            foreach (Pair sessionValue in sessionValues)
            {
                mock.SetupGet(p => p.HttpContext.Session[sessionValue.First as string]).Returns(sessionValue.Second);
            }

            // Arrange
            var controller = CreateSpaceController();
            controller.ControllerContext = mock.Object;

            return controller;
        }

This tells the controller context to use the mock session object and also to use the supplied values for the expected get calls.  The actual test then consumes this builder method like so.

        [TestMethod]
        public void IndexAction_For_Space_1_Page_2_PageSize_2_Should_Have_PageIndex_1_PageSize_2_TotalCount_9()
        {
            var controller = CreateSpaceControllerAs(FakeForumData.FakeUser.UserName, new List<Pair>(new[]{
                 new Pair{
                      //Session Name
                      First = "SpaceController!Index!PageSize",
                      //Session Value
                      Second = 2
                 }
            }));
            // Act
            //Get first page
            ViewResult result = (ViewResult)controller.Index(1,2);

            IPagedModel model = (IPagedModel)result.ViewData.Model;

            // Assert
            Assert.AreEqual(1, model.PageIndex);
            Assert.AreEqual(2, model.PageSize);
            Assert.AreEqual(9, model.TotalCount);
        }

I am not saying I think this is the best method for storing varying page size personalization by the user, but it is something which has got me mocking the session state successfully so I am happy with that!  I hope this is of some use to others making their way though ASP.NET MVC!

Cheers

Andrew

Published Monday, August 10, 2009 4:44 PM by REA_ANDREW
Filed under:

Comments

No Comments