Cómo MOQ una propiedad indexada

81

Estoy intentando simular una llamada a una propiedad indexada. Es decir, me gustaría moq lo siguiente:

object result = myDictionaryCollection["SomeKeyValue"];

y también el valor del setter

myDictionaryCollection["SomeKeyValue"] = myNewValue;

Hago esto porque necesito burlarme de la funcionalidad de una clase que usa mi aplicación.

¿Alguien sabe cómo hacer esto con MOQ? Probé variaciones de lo siguiente:

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

Pero eso no se compila.

¿Es posible lo que estoy tratando de lograr con MOQ? ¿Alguien tiene algún ejemplo de cómo puedo hacerlo?

Ceniza
fuente
¿No sería más fácil usar un objeto stub? Establezca los valores requeridos y verifique los índices que necesita.
Søren Spelling Lund

Respuestas:

94

No está claro lo que está tratando de hacer porque no muestra la declaración del simulacro. ¿Estás intentando burlarte de un diccionario?

MyContainer[(string s)] no es válido C #.

Esto compila:

var mock = new Mock<IDictionary>();
mock.SetupGet( p => p[It.IsAny<string>()]).Returns("foo");
Mike Scott
fuente
1
esto también funciona. excepto que la nueva sintaxis usa en SetupSetlugar de ExpectGet. también funciona para setters.
tmont
1
El IDictionary es de System.Collections y no de System.Collections.Generic
upInCloud
20

Ash, si quieres tener una sesión HTTP simulada, entonces este fragmento de código hace el trabajo:

/// <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);
    }
}
wasker
fuente
Bonito, esto era justo lo que necesitaba +1
Ian Oxley
1
¿Qué parte de este código hace que el código bajo prueba use la sesión simulada? ¿Es TestSuiteBaseuna biblioteca o tu propia clase?
StuperUser
Eso no es una burla, es una falsificación. Solo digo.
Chris Peacock
17

A medida que ha detectado correctamente, hay métodos distintos SetupGety SetupSetpara inicializar captadores y definidores, respectivamente. Aunque SetupGetestá destinado a ser utilizado para propiedades, no para indexadores, y no le permitirá manejar la clave que se le pasa. Para ser precisos, los indexadores SetupGetllamarán de Setuptodos modos:

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

Para responder a su pregunta, aquí hay una muestra de código que usa subyacente Dictionarypara almacenar valores:

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

Como puede ver, debe especificar explícitamente la clave para configurar el definidor del indexador. Los detalles se describen en otra pregunta SO: Moq una propiedad indexada y use el valor del índice en la devolución / devolución de llamada

Vitaliy Ulantikov
fuente
1
¡¡Esto es lo que estaba buscando!! Ninguno de los otros ejemplos funcionó
Fiona - myaccessible.website
8

No es tan difícil, pero me tomó un poco encontrarlo :)

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

Parece que lo que estaba intentando hacer con MOQ no es posible.

Básicamente, estaba intentando MOQ un objeto de tipo HTTPSession, donde la clave del elemento que se establece en el índice solo se puede determinar en tiempo de ejecución. Se necesita acceso a la propiedad indexada para devolver el valor que se estableció previamente. Esto funciona para índices basados ​​en números enteros, pero los índices basados ​​en cadenas no funcionan.

Ceniza
fuente