2012-06-19 23 views
8

Tengo un método asíncrono Estoy convirtiendo a un método de sincronización utilizando un bloqueo de cuenta atrás. Estoy luchando con escribir una prueba unitaria sin usar la función de tiempo de espera de mockito. No puedo encontrar la manera de obtener el método de verificar que esperar a que la llamada al método asincrónico:Mockito con Java async-> sync converter

public interface SyncExchangeService { 
    boolean placeOrder(Order order); 
} 
public interface ExchangeService { 
    void placeOrder(Order order, OrderCallback orderResponseCallback); 
} 

public interface OrderCallback { 
    public void onSuccess(); 
    public void onFailure(); 
} 



public class SyncExchangeServiceAdapter implements SyncExchangeService { 
    private ExchangeService exchangeService; 

    public SyncExchangeServiceAdapter(ExchangeService exchangeService) { 
     this.exchangeService = exchangeService; 
    } 

    @Override 
    public boolean placeOrder(Order order) { 

     final CountDownLatch countdownLatch=new CountDownLatch(1); 
     final AtomicBoolean result=new AtomicBoolean(); 
     exchangeService.placeOrder(order, new OrderCallback() { 

      @Override 
      public void onSuccess() { 
       result.set(true); 
       countdownLatch.countDown(); 
      } 

      @Override 
      public void onFailure(String rejectReason) { 
       result.set(false); 
       countdownLatch.countDown(); 
      } 
     }); 
     try { 
      countdownLatch.await(); 
     } catch (InterruptedException e) { 
      throw new RuntimeException(e); 
     } 
     return result.get(); 
    } 
} 


public class SyncExchangeServiceAdapterTest { 
    private ExchangeService mockExchange=mock(ExchangeService.class); 
    private SyncExchangeServiceAdapter adapter=new SyncExchangeServiceAdapter(mockExchange); 
    private Boolean response; 
    private ArgumentCaptor<Boolean> callback=CaptorArgumentCaptor.forClass(OrderCallback.class); 
    private CountDownLatch latch=new CountDownLatch(1); 


    @Test 
    public void testPlaceOrderWithSuccess() throws Exception { 
     final Order order=mock(Order.class); 
     Executors.newSingleThreadExecutor().submit(new Runnable() { 
      @Override 
      public void run() { 
       response=adapter.placeOrder(order); 
       latch.countDown(); 
      } 
     }); 
      verify(mockExchange,timeout(10)).placeOrder(eq(order), callbackCaptor.capture()); 
//the timeout method is not really recommended and could also fail randomly if the thread takes more than 10ms 


     callbackCaptor.getValue().onSuccess(); 
     latch.await(1000,TimeUnit.MILLISECONDS); 
      assertEquals(true,response); 
    } 


} 

Respuesta

2

Para este tipo de pruebas Me gusta usar una pequeña biblioteca llamada awaitility. Puedes hacerlo tú mismo con un pestillo de cuenta atrás, pero como has visto, tienes que hackear tu prueba con un machete para que funcione.

En esta prueba debe llamar a verificar después de haber esperado el pestillo.

Otro problema en su código es el private Boolean response. Como lo está cambiando en otro subproceso, debe convertirlo en AtomicBoolean o al menos declararlo volatile.

+1

+1 para aguardar. Nunca había oído hablar de él, pero parece bastante útil. – jhericks

-1

no estoy seguro le he entendido bien. si quieres probar que un hilo espera indefinidamente hasta que otro hilo haga algo, yo diría que no puedes hacerlo. porque eso significa que estás preguntando si el programa termina. en cambio, puedes hacer dos cosas.

  1. realiza pruebas concurrentes regulares (por definición, es aleatorio y no te da la certeza de que el código es correcto). usted crea una prueba compleja con dos hilos que usa bloqueos y simula el servicio y usa el método yeld(). en secciones críticas puede probar si no hay un orden incorrecto. por supuesto, usted tiene que ejecutar muchas veces lo que le llevará más de 10 ms
  2. asumir que CountDownLatch funciona correctamente, burlarse de él y comprobar si sus funciones se llaman en el orden correcto
+0

No estoy muy seguro de a qué te refieres ... Estoy buscando una forma determinista de probar la clase anterior. es decir, SIEMPRE producirá el mismo resultado independientemente de si lo ejecuta una vez o un millón de veces. En este momento hay una condición de carrera por lo que podría fallar. –

+0

Además, sin reestructurar la clase no se puede burlar CountDownLatch ya que no se pasa como una dependencia. –

+0

ese es mi punto. simplemente no puede probar el código de subprocesos múltiples de una manera determinista para decir: 'es correcto'. no puedes probar si un hilo espera hasta otro [http://en.wikipedia.org/wiki/Halting_problem]. y, por supuesto, puede simular countDownLatch, ver powermock y whitebox – piotrek