Christian Weiss

random thoughts on software development

Testable and reusable cookie access with ASP.Net MVC RC

All good things come in threes, so I’m writing another post about how to access cookie or sessions. I got inspired by a comment from Chris Marisic, who suggested to use a more testable way for this stuff!

Previous posts about this topic:

Intro

Using static wrapper properties is a quick and easy way, but you can’t unit test them because they access HttpContext.Current directly. This time I will show you, how you can build a fully unit testable and strongly typed way to access your cookies. As there has been Christmas time 2 days ago (ASP.Net MVC RC1 was released *g*) I’m using the latest MVC bits for my example!

A Reusable Cookie Container

The cookie container is responsible for getting values out of and into the cookie collections. It does not know which concrete values I’m using in my application! This is implemented in a different level, so you can use this class for all of your applications!

In my implementation it’s possible to store “objects” in cookies. I’ve implemented it this way because I don’t want to convert all my DateTime, int, … cookies every time! But I also don’t want someone to save Lists or any other complex types, so my SetValue() method validates the type of the value and throws an exception, if it’s not a value type or nullable value type. That’s a little type checking, but I think it is worth it because cookies are set quite rarely!

Here’s the interface:

public interface ICookieContainer
{
    bool Exists(string key);
    
    string GetValue(string key);
    T GetValue<T>(string key);
    
    void SetValue(string key, object value, DateTime expires);
}

I will just show the fundamental code here. If you want to see the whole implementation, please take a look at the code sample. (see bottom)

As you can see below, I’ve used the abstracted versions of HttpRequest and HttpResponse, which you get, if you use ASP.Net MVC. That’s just one of thousand things I love about ASP.Net MVC. These classes can be used easily in unit tests. Notice that everything can be injected here. There’s no direct access to HttpContext.Current!

public class CookieContainer : ICookieContainer
{
    private readonly HttpRequestBase _request;
    private readonly HttpResponseBase _response;

    public CookieContainer(HttpRequestBase request, HttpResponseBase response)
    {
        // "Check" is a helper class, I've got from the "Kigg" project
        Check.IsNotNull(request, "request");
        Check.IsNotNull(response, "response");

        _request = request;
        _response = response;
    }

    public string GetValue(string key)
    {
        Check.IsNotEmpty(key, "key");

        HttpCookie cookie = _request.Cookies[key];
        return cookie != null ? cookie.Value : null;
    }

    public void SetValue(string key, object value, DateTime expires)
    {
        Check.IsNotEmpty(key, "key");

        string strValue = CheckAndConvertValue(value);

        HttpCookie cookie = new HttpCookie(key, strValue) {Expires = expires};
        _response.Cookies.Set(cookie);
    }
    
    // ... see code sample for full implementation
}

Here’s a sample unit tests that proves the testability of this code. I use Moq as my mocking framework.

public static class Mocks
{
    public static Mock<HttpRequestBase> HttpRequest()
    {
        var httpRequest = new Mock<HttpRequestBase>();
        httpRequest.Setup(x => x.Cookies).Returns(new HttpCookieCollection());
        return httpRequest;
    }

    public static Mock<HttpResponseBase> HttpResponse()
    {
        var httpResponse = new Mock<HttpResponseBase>();
        httpResponse.Setup(x => x.Cookies).Returns(new HttpCookieCollection());
        return httpResponse;
    }
}

// This method is from my CookieContainerTests class

[TestMethod]
public void SetValue_UpdatesExistingCookie()
{
    // Arrange
    const string cookieName = "myCookie";
    const string cookieValue = "myValue";
    DateTime cookieExpires = new DateTime(2009, 1, 1, 0, 0, 0);

    var httpRequest = Mocks.HttpRequest();
    var httpResponse = Mocks.HttpResponse();
    var cookieContainer = new CookieContainer(httpRequest.Object, httpResponse.Object);
    
    httpResponse.Object.Cookies.Add(new HttpCookie(cookieName, "oldValue"));

        // Act
    _cookieContainer.SetValue(cookieName, cookieValue, cookieExpires);

    // Assert
    HttpCookie cookie = httpResponse.Object.Cookies["myCookie"];
    Assert.IsNotNull(cookie);
    Assert.AreEqual(cookie.Name, cookieName);
    Assert.AreEqual(cookie.Value, cookieValue);
    Assert.AreEqual(cookie.Expires, cookieExpires);
}

That’s it! Now you have a testable and reusable cookie container!

How to use it in your application

It’s really easy to integrate this into your app! Just create an interface that defines all your application-specific properties you want to save in cookies and a concrete implementation of this interface that interacts with the cookie container.

public interface IAppCookies
{
    string UserEmail { get; set; }
    DateTime? LastVisit { get; set; }
}

public class AppCookies : IAppCookies
{
    private readonly ICookieContainer _cookieContainer;

    public AppCookies(ICookieContainer cookieContainer)
    {
        _cookieContainer = cookieContainer;
    }

    public string UserEmail
    {
        get { return _cookieContainer.GetValue("UserEmail"); }
        set { _cookieContainer.SetValue("UserEmail", value, DateTime.Now.AddDays(10)); }
    }

    public DateTime? LastVisit
    {
        get { return _cookieContainer.GetValue<DateTime?>("LastVisit"); }
        set { _cookieContainer.SetValue("LastVisit", value, DateTime.Now.AddDays(10)); }
    }
}

You can now inject this IAppCookies interface to your MVC Controller:

public class HomeController : Controller
{
    private readonly IAppCookies _cookies;

    public HomeController(IAppCookies cookies)
    {
        _cookies = cookies;
    }

    public ActionResult Index()
    {
        DateTime currentTime = DateTime.Now;

        IndexViewModel viewModel = new IndexViewModel
        {
            CurrentTime = currentTime,
            LastVisit = (_cookies.LastVisit ?? currentTime),
            UserEmail = _cookies.UserEmail
        };

        _cookies.LastVisit = currentTime;

        return View(viewModel);
    }

    public class IndexViewModel
    {
        public string UserEmail { get; set; }
        public DateTime LastVisit { get; set; }
        public DateTime CurrentTime { get; set; }
    }
}

Wow, you’re still reading :-)

That’s all I want to show here! If you want to see more about how the IOC is set up (I’m using StructureMap) or anything else, take a look at the full code:

I looking forward to hearing your feedback on this!

Thanks for reading,
Christian Weiss

Christian Weiss
Written by Christian Weiss
Christian Weiss is working as a technical lead at Lyoness, a global loyalty rewards program. He lives in Austria and builds web-based software with Microsoft technologies since more than 10 years. You can reach him on or on .

Comments (4) -

  • Chris Marisic

    1/30/2009 1:57:05 PM |

    Christian,

    This was a great write up, it's been a long time since I've used MVC (before the Http Request/Response Bases were included) so it didn't cross my mind offhand that you can use the base classes to be able to mock the container entirely instead of needing to wrap the container in an interface like you would in webforms.

    I added you to my blogroll I'll look forward to see what you post next!

  • Christian Weiss

    1/31/2009 12:37:40 PM |

    thanks for adding me to your blogroll! You're now on my blogroll too. ;)

  • Phil

    3/23/2009 4:39:43 PM |

    Hi CW

    Are you able to fix/confirm/repost the link for your sourcecode? I am having some difficulty with the last part of your article where the controller has a constructor taking in a ICookies interface. Not sure where to add the new ICookies when instantiating the controller.

    Sorry to bug you if it's just my problem!

Comments are closed