Mocking an indexer property with Moq is nearly but not quite straightforward. SetUpProperty() doesn't work but if you only need to moq write/read to one or two keys or you can use this code from a stackoverflow post by seanlinmt for each key:
httpContext.SetupSet(x => x.Session["key"] = It.IsAny<object>())
.Callback((string name, object value) =>
{
httpContext.SetupGet(x => x.Session[name]).Returns(value);
});
The missing functionality is that you have to setup for each specific key you are going to access. You can't do this to setup all in one:
httpContext.SetupSet(x => x.Session[ /**/ It.IsAny<string>() /**/ ]
= It.IsAny<object>()). ....
If you're mocking out SessionState or ApplicationState you may want to mock multiple keys even for a single test fixture, but don't want dozens of lines of setup code for each key you use.
You can at least abbreviate by shipping out the setup to an extension method so you can do this:
mockSessionStateBase.SetUpIndexerForKeys("a", "b", "c");
Like this:
public static class MoqStateDictionaryExtension
{
private static Dictionary<Mock, Dictionary<string, object>> FakeStateDictionaries= new Dictionary<Mock, Dictionary<string, object>>();
public static void SetUpIndexerForKeys(this Mock<HttpApplicationStateBase> @this, params string[] keys)
{
EnsureDictionary(@this);
foreach (var key in keys)
{
string key1 = key;
@this.SetupSet(
x => x[key1] = It.IsAny<object>()
)
.Callback<string, object>(
(name, val) =>
{
FakeStateDictionaries[@this][name] = val;
@this.SetupGet(x => x[name]).Returns(FakeStateDictionaries[@this][name]);
}
);
}
}
public static void SetUpIndexerForKeys(this Mock<HttpSessionStateBase> @this, params string[] keys)
{
EnsureDictionary(@this);
foreach (var key in keys)
{
string key1 = key;
@this.SetupSet(
x => x[key1] = It.IsAny<object>()
)
.Callback<string, object>(
(name, val) =>
{
FakeStateDictionaries[@this][name] = val;
@this.SetupGet(x => x[name]).Returns(FakeStateDictionaries[@this][name]);
}
);
}
}
private static void EnsureDictionary(Mock mock)
{
if (!FakeStateDictionaries.ContainsKey(mock)) { FakeStateDictionaries[mock] = new Dictionary<string, object>(); }
}
public static void RemoveDictionary(Mock mock)
{
if (FakeStateDictionaries.ContainsKey(mock)) { FakeStateDictionaries.Remove(mock); }
}
public static void RemoveDictionaries()
{
FakeStateDictionaries = new Dictionary<Mock, Dictionary<string, object>>();
}
}
Used like this:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web;
using Moq;
using NUnit.Framework;
namespace MockingIndexersWithMoq
{
[TestFixture]
public class ApplicationWrapperTests
{
private Mock<HttpApplicationStateBase> mockApplication;
private HttpApplicationStateBase exampleUsage;
[SetUp]
public void SetUp()
{
mockApplication = new Mock<HttpApplicationStateBase>();
exampleUsage = mockApplication.Object;
}
[TearDown]
public void ClearFakeStateDictionaries()
{
MoqStateDictionaryExtension.RemoveDictionary(mockApplication);
}
[Test]
public void TestWhichReliesOnMockedIndexer()
{
mockApplication.SetUpIndexerForKeys("a", "b", "c");
exampleUsage["a"] = 1;
exampleUsage["a"].ShouldEqual(1);
exampleUsage["a"] = 2;
exampleUsage["a"].ShouldEqual(2);
exampleUsage["b"] = 1;
exampleUsage["b"].ShouldEqual(1);
exampleUsage["c"].ShouldBeNull();
exampleUsage["d"].ShouldBeNull();
exampleUsage["e"].ShouldBeNull();
}
}
}