2012-05-23 10 views
18

Estoy buscando cómo puedo simular un método que devuelve un valor diferente la segunda vez que se llama al primero hora. Por ejemplo, algo como esto:RhinoMocks: simulando un método cuyo valor de retorno cambia (incluso cuando pasó el mismo parámetro) con llamadas múltiples

public interface IApplicationLifetime 
{ 
    int SecondsSinceStarted {get;} 
} 

[Test] 
public void Expected_mock_behaviour() 
{ 
    IApplicationLifetime mock = MockRepository.GenerateMock<IApplicationLifetime>(); 

    mock.Expect(m=>m.SecondsSinceStarted).Return(1).Repeat.Once(); 
    mock.Expect(m=>m.SecondsSinceStarted).Return(2).Repeat.Once(); 

    Assert.AreEqual(1, mock.SecondsSinceStarted); 
    Assert.AreEqual(2, mock.SecondsSinceStarted); 
} 

¿Hay algo que haga esto posible? Además de implementar un sub para el getter que implementa una máquina de estado?

Saludos muchachos,

Alex

+0

Eche un vistazo a los simulacros ordenados y desordenados: http://ayende.com/wiki/Rhino%20Mocks%20Ordered%20and%20Unordered.ashx – Maciej

+2

¿Qué pasa con el código que proporcionó? 'Repeat.Once()' debería funcionar –

+0

@lazyberezovsky La primera llamada a m.SeconsSinceStarted devuelve 2, no 1 como se esperaba – AlexC

Respuesta

28

Puede interceptar valores de retorno con .WhenCalled método. Tenga en cuenta que usted todavía tiene que proporcionar un valor medio de .Return método, sin embargo Rhino simplemente ignorarlo si ReturnValue se altera de invocación de método:

int invocationsCounter = 1; 
const int IgnoredReturnValue = 10; 
mock.Expect(m => m.SecondsSinceLifetime) 
    .WhenCalled(mi => mi.ReturnValue = invocationsCounter++) 
    .Return(IgnoredReturnValue); 

Assert.That(mock.SecondsSinceLifetime, Is.EqualTo(1)); 
Assert.That(mock.SecondsSinceLifetime, Is.EqualTo(2)); 

Editar:

escarbar un poco más, parece que .Repeat.Once()qué trabaja de hecho en este caso y se puede utilizar para lograr mismo resultado:

mock.Expect(m => m.SecondsSinceStarted).Return(1).Repeat.Once(); 
mock.Expect(m => m.SecondsSinceStarted).Return(2).Repeat.Once(); 
mock.Expect(m => m.SecondsSinceStarted).Return(3).Repeat.Once(); 

Devolverá 1, 2, 3 en llamadas consecutivas.

+0

Según tengo entendido, para algo que no sean números secuenciales, ¿debería crear una matriz y devolver los valores por índice? –

+0

Acepto que este método funciona, pero esto es esencialmente lo mismo que tener que implementar una máquina de estado, lo que mencioné que estoy tratando de evitar (es decir,si quisiera devolver 11 en la segunda llamada, entonces tendría que hacer algo como "if (invocationsCounter == 1) return 1; else if (invocationsCounter == 2) return 11;" que se parece mucho a una máquina de estado. – AlexC

+0

Re: su edición. No estoy seguro de si esta es alguna funcionalidad que ha cambiado en la versión de RhinoMocks que estoy usando y la que está usando, pero he ejecutado la prueba que está en mi pregunta original, y puedo decirle que falla (es decir, la primera llamada) a SegundosDesdeStarted devuelve 2). ¿Tienes un enlace a donde encontraste que funciona? – AlexC

0

¿Es el tiempo el factor determinante en lo que devuelve el método? Si es así, trataría de abstraer el pasaje del tiempo para que tenga control directo sobre él en sus pruebas.

Algo así como una interfaz llamada ITimeProvider con un miembro llamado GetElapsedTime() y luego crea una implementación predeterminada para usar en toda la aplicación.

Luego, cuando esté probando, pase un ITimeProvider burlado para que controle lo que percibe su aplicación al pasar el tiempo.

+0

Realmente, el tiempo fue solo un ejemplo útil sin describir completamente qué es lo que estoy tratando de probar. Sin embargo, aun así, si quisiera burlarme de un ITimeProvider, que tenía un método GetElapsedTime(), el problema seguiría existiendo si quisiera escribir una prueba unitaria que llamara GetElapsedTime() dos veces, y quisiera que mi simulador ITimeProvider devolviera un tiempo para cada llamada – AlexC

+0

¿Podría hacer que ITimeProvider sea una dependencia de la propiedad? ¿De este modo ser capaz de inyectar nuevos simulacros antes de cada llamada? Aun así, si no es hora de que se trate, mi respuesta probablemente sea inútil ... – theMothaShip

+0

Mi pregunta era más acerca de si RhinoMocks admite este tipo de interacción con los burms/stubs generados, y menos acerca de cómo podría solucionarlo, no debería ser compatible. Gracias sin embargo. – AlexC

5

Utilice simplemente

mock.Expect(m=>m.SecondsSinceStarted).Return(1).Repeat.Once(); 
mock.Expect(m=>m.SecondsSinceStarted).Return(2).Repeat.Once(); 

Esto devolverá 1 durante la primera llamada, y 2 durante la segunda llamada. Al menos en RhinoMocks 3.6.0.0

+0

Esto ciertamente entra en conflicto con lo que veo cuando ejecuto la prueba anterior – AlexC

+0

Como mencioné en la respuesta aceptada, esto es correcto, mi prueba solo estaba fallando ya que estaba dando un paso atrás y evaluando algo inadvertidamente. Upvoted. – AlexC

Cuestiones relacionadas