DevelopMENTAL Madness

Thursday, February 26, 2009

Unit Testing: Moq doesn’t support property indexers

I hit a road block today writing some tests which were dependent upon another class’s property indexer. When trying to do the following using Moq:

   1: [TestMethod]
   2: public void Test1()
   3: {
   4:     Mock<HttpContextBase> context = new Mock<HttpContextBase>();
   5:  
   6:     Object actual = null;
   7:  
   8:     context.ExpectSet(c => c.Session["SomeKey"]).Callback(o => actual = o);
   9:  
  10:     context.Object.Session["SomeKey"] = "some value";
  11:     
  12:     Assert.IsNotNull(actual, "Value was not set");
  13: }

Line 8 fails with this error:

System.ArgumentException: Expression is not a property access: c => c.Session.get_Item("SomeKey").

It appears the current stable build of Moq (version 2.6.1014.1) doesn’t support property indexers. The good news is that a few days ago support was added for property indexers. However, if you’re like me and you’re not working with the latest and greatest, then there’s a workaround. Just create your own mock implementation of the class with the indexed property. In my case the class in question was HttpSessionStateBase. Here’s my mock implementation:

/// <summary>
/// Mock implementation of HttpSessionStateBase
/// </summary>
/// <remarks>
/// Current stable build of Moq (2.6.1014.1) doesn't support property indexers 
/// at this time, so this is an implementation of HttpSessionStateBase 
/// which allows us to verify that the session cache is being updated.
/// </remarks>
internal class MockSession : HttpSessionStateBase
{
    Dictionary<string, object> session = new Dictionary<string, object>();
 
    public override void Add(string name, object value)
    {
        session.Add(name, value);
    }
 
    public override object this[string name]
    {
        get
        {
            if (!session.ContainsKey(name))
                return null;
 
            return session[name];
 
        }
        set
        {
            session[name] = value;
        }
    }
}

Then you can do the following:

// setup
MockSession session = new MockSession();
Mock<HttpContextBase> context = new Mock<HttpContextBase>();
context.ExpectGet(c => c.Session).Returns(session);
 
// exercise
context.Session["SomeKey"] = "some value";
 
// assert
Assert.IsNotNull(context.Session["SomeKey"]);

Happy Moqing!

Labels: ,