如何最小起订量索引属性

我试图模拟一个索引属性的调用,也就是说,我想模拟以下内容:

object result = myDictionaryCollection["SomeKeyValue"];

还有 setter 值

myDictionaryCollection["SomeKeyValue"] = myNewValue;

我这样做是因为我需要模拟我的应用程序使用的类的功能。

有人知道如何使用最小起订量吗? 我已经尝试了以下变化:

Dictionary<string, object> MyContainer = new Dictionary<string, object>();
mock.ExpectGet<object>( p => p[It.IsAny<string>()]).Returns(MyContainer[(string s)]);

但是没有编译。

我正在努力实现的最小起订量是可能的,有人有任何例子,我如何可以做到这一点?

32227 次浏览

It's not clear what you're trying to do because you don't show the declaration of the mock. Are you trying to mock a dictionary?

MyContainer[(string s)] isn't valid C#.

This compiles:

var mock = new Mock<IDictionary>();
mock.SetupGet( p => p[It.IsAny<string>()]).Returns("foo");

It appears that what I was attempting to do with MOQ is not possible.

Essentially I was attempting to MOQ a HTTPSession type object, where the key of the item being set to the index could only be determined at runtime. Access to the indexed property needed to return the value which was previously set. This works for integer based indexes, but string based indexes do not work.

Ash, if you want to have HTTP Session mock, then this piece of code does the job:

/// <summary>
/// HTTP session mockup.
/// </summary>
internal sealed class HttpSessionMock : HttpSessionStateBase
{
private readonly Dictionary<string, object> objects = new Dictionary<string, object>();


public override object this[string name]
{
get { return (objects.ContainsKey(name)) ? objects[name] : null; }
set { objects[name] = value; }
}
}


/// <summary>
/// Base class for all controller tests.
/// </summary>
public class ControllerTestSuiteBase : TestSuiteBase
{
private readonly HttpSessionMock sessionMock = new HttpSessionMock();


protected readonly Mock<HttpContextBase> Context = new Mock<HttpContextBase>();
protected readonly Mock<HttpSessionStateBase> Session = new Mock<HttpSessionStateBase>();


public ControllerTestSuiteBase()
: base()
{
Context.Expect(ctx => ctx.Session).Returns(sessionMock);
}
}

Its not that difficult but it took a little bit to find it :)

var request = new Moq.Mock<HttpRequestBase>();
request.SetupGet(r => r["foo"]).Returns("bar");

As you correctly spotted, there are distinct methods SetupGet and SetupSet to initialize getters and setters respectively. Although SetupGet is intended to be used for properties, not indexers, and will not allow you handling key passed to it. To be precise, for indexers SetupGet will call Setup anyway:

internal static MethodCallReturn<T, TProperty> SetupGet<T, TProperty>(Mock<T> mock, Expression<Func<T, TProperty>> expression, Condition condition) where T : class
{
return PexProtector.Invoke<MethodCallReturn<T, TProperty>>((Func<MethodCallReturn<T, TProperty>>) (() =>
{
if (ExpressionExtensions.IsPropertyIndexer((LambdaExpression) expression))
return Mock.Setup<T, TProperty>(mock, expression, condition);
...
}
...
}

To answer your question, here is a code sample using underlying Dictionary to store values:

var dictionary = new Dictionary<string, object>();


var applicationSettingsBaseMock = new Mock<SettingsBase>();
applicationSettingsBaseMock
.Setup(sb => sb[It.IsAny<string>()])
.Returns((string key) => dictionary[key]);
applicationSettingsBaseMock
.SetupSet(sb => sb["Expected Key"] = It.IsAny<object>())
.Callback((string key, object value) => dictionary[key] = value);

As you can see, you have to explicitly specify key to set up indexer setter. Details are described in another SO question: Moq an indexed property and use the index value in the return/callback