2011-05-31 8 views
6

Tengo algunos problemas con Moq. La siguiente prueba unitaria arroja una excepción, aunque se invocará el método correspondiente.El método Moq verify falla aunque el método se llame

[TestMethod] 
public void CreateFinishTest() { 
    // mock methods 
    factoryMock.Setup(f => f.LoadPlan("TestPlanDoNotUse")).Returns(testPlan).Verifiable(); 
    factoryMock.Setup(f => f.CreateFinish(It.IsAny<CreateFinishMessage>(), It.IsAny<string>())).Returns(testFinish.Id).Verifiable(); 

    try { 
     var cfm = new CreateFinishMessage() { 
      ClientId = 11, 
      MessageId = 23456, 
      CustomerId = 6, 
      FinishName = "MyFinish", 
      PlanId = "TestPlanDoNotUse" 
     }; 
     var cmd = sysCfg.Executor.CreateFinish(cfm); // calls LoadPlan with cfm.PlanId and CreateFinish with cfm and cfm.PlanId 
     sysCfg.Executor.Execute(cmd); 

     factoryMock.Verify(f => f.LoadPlan("TestPlanDoNotUse"), Times.Exactly(1)); 
     factoryMock.Verify(f => f.CreateFinish(It.IsAny<CreateFinishMessage>(), It.IsAny<string>()), Times.Exactly(1)); 
    } catch (Exception exc) { 
     Assert.Fail(exc.Message); 
    } 
} 

Este error se produce:

Expected invocation on the mock exactly 1 times, but was 0 times: f => f.LoadPlan("TestPlanDoNotUse") 

Configured setups: 
f => f.LoadPlan("TestPlanDoNotUse"), Times.Once 

Performed invocations: 
IFactory.LoadPlan("TestPlanDoNotUse") 
Factory.CreateFinish(IndiValue.LiveMarket.IndiCore.Communication.MessagingFormat.CreateFinishMessage, "MyFinish") 

He intentado varias diferentes Verificar-llamadas, pero no va a funcionar. Y el error que se produce parece bastante confuso, dice que nunca se llama al LoadPlan("TestPlanDoNotUse"), pero aparece en la lista @ Invocaciones realizadas.

Problema resuelto:

creo que he encontrado el problema, que no era un problema Moq. En sysCfg.Executor.CreateFinish(cfm), se creó y se inició un nuevo hilo. Este hilo no se terminó y por lo tanto factoryMock.Verify(...) falló.

que utilizan AutoResetEvents:

// create AutoResetEvent triggers 
AutoResetEvent m_testTrigger1 = new AutoResetEvent(false); 

// mock methods  
factoryMock.Setup(f => f.LoadPlan(It.IsAny<string>())).Returns(testPlan).Callback(() => m_testTrigger1.Set()); 

// do something 

// wait for triggers 
bool didReturn1 = m_testTrigger1.WaitOne(timeOut); 

Respuesta

6

En el que no se puede llamar a Verificable, es importante que los argumentos en su expectativa coincidan con los argumentos que están siendo utilizados por el código de producción.

En cuanto al uso de Thread.Sleep, evítelo siempre que sea posible, ya que solo ralentizará las pruebas para adaptarse a la máquina más lenta. Normalmente introduzco WaitHandles en mis pruebas para asegurarme de que las pruebas se ejecuten tan rápido como el código.

Tome un peek here on a small utility que utiliza WaitHandles con eventos.

+0

Usé AutoResetEvents: – Robar

6

Por lo general no utiliza verificable() en sus configuraciones en conjunto con los métodos Verify (expr, veces). ¿Funciona si elimina las llamadas .Verificables()?

+0

Si quito el .Verifiable() llamadas obtengo un error de seguimiento: 'Invocación esperada en el simulacro exactamente 1 vez, pero fue 0 veces: f => f.CreateFinish (It.IsAny (), It.IsAny Set configurado PS: f => f.CreateFinish (It.IsAny (), It.IsAny ()), Times.Never invocaciones realizadas: IFactory.LoadPlan ("TestPlanDoNotUse") ' – Robar

+0

que se parece un poco mejor, si ese es de hecho el mensaje de error completo. Ahora parece que CreateFinish simplemente no se llama. ¿Dice algo acerca de las invocaciones realizadas para CreateFinish? – TheFogger

+0

No, no dijo nada acerca de la invocación de CreateFinish, pero lo depuré y creo que encontré el problema: ver mi respuesta. Gracias por ayudar – Robar

2

Supongo que esta es una respuesta para mí también, pero creo que esta es una solución más simple que muchas de las mencionadas anteriormente.

he implementado una función WaitFor que utiliza una devolución de llamada lambda para evaluar una condición:

public static void WaitFor(Func<bool> action, long timeoutMillis = 10000) { Stopwatch elapsed = Stopwatch.StartNew(); elapsed.Start(); // ReSharper disable once LoopVariableIsNeverChangedInsideLoop while (!action()) { if (elapsed.ElapsedMilliseconds > timeoutMillis) { throw new TimeoutException("Timed out waiting for condition to become true after " + elapsed.ElapsedMilliseconds + " ms"); } Thread.Sleep(0); } }

y el código de prueba es el siguiente:

[Test] 
    public void ShouldNackUnparsableInboundMessage() 
    { 
     var nackCalled = false; 
     _mock.Setup(m => m.BasicNack(999, false, false)).Callback(() => 
     { 
      nackCalled = true; 
     }); 

     ... do whatever which invokes the call on another thread. 

     WaitFor(() => nackCalled); 
     // Test will fail with timeout if BasicNack is never called. 
    } 
Cuestiones relacionadas