web-dev-qa-db-fra.com

Comment MOQ une propriété indexée

J'essaie de simuler un appel à une propriété indexée. C'est à dire. Je voudrais moq ce qui suit:

object result = myDictionaryCollection["SomeKeyValue"];

et aussi la valeur setter

myDictionaryCollection["SomeKeyValue"] = myNewValue;

Je fais cela parce que je dois me moquer des fonctionnalités d'une classe que mon application utilise.

Est-ce que quelqu'un sait comment faire cela avec MOQ? J'ai essayé des variations sur les éléments suivants:

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

Mais cela ne compile pas.

Est-ce que j'essaie de réaliser avec MOQ, est-ce que quelqu'un a des exemples de comment je peux le faire?

69
Ash

Ce que vous essayez de faire n'est pas clair parce que vous ne montrez pas la déclaration de la maquette. Essayez-vous de vous moquer d'un dictionnaire?

MyContainer[(string s)] n'est pas valide C #.

Cela compile:

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

Ash, si vous voulez avoir une simulation de session HTTP, alors ce morceau de code fait le travail:

/// <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);
    }
}
20
wasker

Comme vous l'avez correctement repéré, il existe des méthodes distinctes SetupGet et SetupSet pour initialiser respectivement les getters et les setters. Bien que SetupGet soit destiné à être utilisé pour les propriétés, pas pour les indexeurs, et ne vous permettra pas de gérer la clé qui lui est transmise. Pour être précis, pour les indexeurs SetupGet appellera Setup quand même:

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);
    ...
  }
  ...
}

Pour répondre à votre question, voici un exemple de code utilisant Dictionary sous-jacent pour stocker des valeurs:

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);

Comme vous pouvez le voir, vous devez spécifier explicitement la clé pour configurer l'indexeur setter. Les détails sont décrits dans une autre SO question: Moq une propriété indexée et utilisez la valeur d'index dans le retour/rappel

9

Ce n'est pas si difficile mais il a fallu un peu de temps pour le trouver :)

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