2012-06-19 6 views
8

Ich habe eine asynchrone Methode, die ich in eine Synchronisierungsmethode mit einem Countdown-Latch konvertieren. Ich habe Schwierigkeiten, einen Komponententest zu schreiben, ohne die Timeout-Funktion von Mockito zu verwenden. Ich kann nicht herausfinden, wie die Verifizierungsmethode für den Asynchron-Methodenaufruf zu warten, um zu erhalten:Mockito mit Java-async-> Sync-Konverter

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


} 

Antwort

2

Für diese Arten von Tests genannt werden Ich benutze gerne eine kleine Bibliothek namens awaitility. Sie können es selbst mit einem Countdown-Latch tun, aber wie Sie gesehen haben, müssen Sie Ihren Test mit einer Machete hacken, um diese Arbeit zu machen.

In diesem Test sollten Sie überprüfen, nachdem Sie die Verriegelung erwartet haben. Ein anderes Problem in Ihrem Code ist private Boolean response. Da Sie es in einem anderen Thread ändern, sollten Sie dies eine AtomicBoolean machen oder zumindest volatile deklarieren.

+1

+1 für die Abwartbarkeit zu erstellen. Ich hatte noch nie davon gehört, aber es sieht sehr nützlich aus. – jhericks

-1

Ich bin nicht sicher ich Dich richtig verstehe. Wenn Sie testen möchten, dass ein Thread unbegrenzt wartet, bis ein anderer Thread etwas tut, würde ich sagen, dass Sie es nicht tun können. weil das bedeutet, dass Sie fragen, ob das Programm beendet wird. Stattdessen können Sie zwei Dinge tun.

  1. machen Sie regelmäßige gleichzeitige Tests (per Definition ist es zufällig und geben Sie nicht Gewissheit, dass der Code korrekt ist). Sie erstellen einen komplexen Test mit zwei Threads, der Sperren verwendet, simuliert den Dienst und verwendet die Methode yeld(). In kritischen Abschnitten können Sie testen, ob es eine falsche Reihenfolge gibt. natürlich muss man es oft laufen, so dass es Sie nehmen mehr als 10ms
  2. davon ausgehen, dass CountDownLatch richtig funktioniert, verspotten sie und prüfen, ob seine Funktionen in der richtigen Reihenfolge
+0

Ich bin mir nicht ganz sicher, was Sie bekommen. Ich bin auf der Suche nach einer deterministischen Art, die Klasse oben zu testen. h. es wird IMMER das gleiche Ergebnis liefern, unabhängig davon, ob Sie es einmal oder eine Million Male laufen. Im Moment gibt es eine Race-Bedingung, so dass es scheitern könnte. –

+0

Auch ohne die Klasse zu restrukturieren, können Sie CountDownLatch nicht vortäuschen, da es nicht als Abhängigkeit übergeben wird. –

+0

das ist mein Punkt. Sie können Multithreading-Code nicht deterministisch testen, um zu sagen: "Es ist richtig". Sie können nicht testen, ob ein Thread auf einen anderen wartet [http://en.wikipedia.org/wiki/Halting_problem]. und natürlich können Sie countDownLatch mock, siehe powermock und whitebox – piotrek