2012-06-19 23 views
8

Ho un metodo asincrono sto convertendo in un metodo di sincronizzazione utilizzando un latch countdown. Sto lottando con la scrittura di un test unitario senza usare la funzione di timeout di mockito. Non riesco a capire come ottenere il metodo di verifica per attendere la chiamata del metodo asincrono:Mockito con convertitore di sincronizzazione asincrono Java

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


} 

risposta

2

Per questi tipi di test Mi piace usare una piccola libreria chiamata awaitility. Puoi farlo da solo con un lucchetto per il conto alla rovescia, ma come hai visto devi eseguire il test con un machete per farlo funzionare.

In questo test è necessario effettuare una verifica dopo aver atteso il latch.

Un altro problema nel codice è private Boolean response. Dal momento che lo stai modificando in un'altra discussione dovresti renderlo uno AtomicBoolean o almeno dichiararlo volatile.

+1

+1 per awaitility. Non ne avevo mai sentito parlare, ma sembra abbastanza utile. – jhericks

-1

non sono sicuro di aver capito correttamente. se vuoi testare che un thread attende indefinitamente fino a quando l'altro thread fa qualcosa, allora direi che non puoi farlo. perché ciò significa che stai chiedendo se il programma termina. invece puoi fare due cose.

  1. fare test concomitanti regolari (per definizione è casuale e non ti danno la certezza che il codice sia corretto). crei un test complesso con due thread che utilizza blocchi e simula il servizio e usa il metodo yeld(). nelle sezioni critiche è possibile verificare se non esiste un ordine errato. naturalmente si deve eseguire molte volte quindi ci vorrà più di 10 ms
  2. supporre che CountDownLatch funziona correttamente, deridere e verificare se le sue funzioni sono chiamate nel giusto ordine
+0

Non sono abbastanza sicuro di quello che stai cercando .. Sto cercando un modo deterministico per testare la classe di cui sopra. Ciò produrrà SEMPRE lo stesso risultato indipendentemente dal fatto che lo si esegua una volta o un milione di volte. Al momento c'è una condizione di gara che potrebbe fallire. –

+0

Anche senza ristrutturare la classe non è possibile simulare CountDownLatch poiché non viene passato come dipendenza. –

+0

questo è il mio punto. semplicemente non è possibile testare il codice multi-thread in modo deterministico per dire: "è corretto". non è possibile testare se un thread attende fino a un altro [http://en.wikipedia.org/wiki/Halting_problem]. e naturalmente puoi prendere in giro countDownLatch, vedere powermock e whitebox – piotrek