2012-06-19 10 views
8

Mam metodę asynchroniczną, którą konwertuję do metody synchronizacji za pomocą zatrzasku odliczającego. Mam problem z napisaniem testu jednostkowego bez użycia funkcji timeout mockito. Nie mogę pracować się, jak uzyskać metoda sprawdzenia czekać na wywołanie metody asynchronicznej:Mockito z asynchronizacją Java-> konwerter synchronizacji

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


} 

Odpowiedz

2

Dla tych rodzajów testów Lubię korzystać z małej biblioteki o nazwie awaitility. Możesz to zrobić samemu z zatrzaskiem odliczającym, ale jak już zauważyłeś, musisz zhakować test maczetą, aby to wykonać.

W tym teście powinieneś sprawdzić weryfikację po oczekiwaniu na zatrzask.

Kolejnym problemem w kodzie jest private Boolean response. Ponieważ zmieniasz go w innym wątku, powinieneś to uczynić AtomicBoolean lub przynajmniej zadeklarować to jako volatile.

+1

+1 dla oczekujących. Nigdy o tym nie słyszałem, ale wygląda na całkiem użyteczne. – jhericks

-1

nie jestem pewien Rozumiem poprawnie. jeśli chcesz przetestować, że jeden wątek czeka w nieskończoność, aż inny wątek coś zrobi, to powiedziałbym, że nie możesz tego zrobić. ponieważ oznacza to, że pytasz, czy program się zakończy. zamiast tego możesz zrobić dwie rzeczy.

  1. przeprowadzać regularne testy równoległe (z definicji są one losowe i nie dają pewności, że kod jest poprawny). Tworzysz złożony test z dwoma wątkami, które używają blokad i symulują usługę i używają metody yeld(). w krytycznych sekcjach możesz sprawdzić, czy nie ma niepoprawnej kolejności. Oczywiście trzeba go uruchomić wiele razy, więc to zajmie Ci więcej niż 10ms
  2. Zakładamy, że CountDownLatch działa poprawnie, drwić go i sprawdź, czy jego funkcje są wywoływane w odpowiedniej kolejności
+0

Nie jestem do końca pewien, do czego zmierzasz. Szukam deterministycznego sposobu testowania powyższej klasy. tj. ZAWSZE przyniesie taki sam wynik, niezależnie od tego, czy uruchomisz go raz czy milion razy. W tej chwili jest sytuacja wyścigowa, więc może zawieść. –

+0

Również bez restrukturyzacji klasy nie można kpić z CountDownLatch, ponieważ nie jest ona przekazywana jako zależność. –

+0

o to mi chodzi. po prostu nie można przetestować wielowątkowego kodu w deterministyczny sposób, aby powiedzieć: "to jest poprawne". nie możesz przetestować, czy jedna wątek czeka na kolejną [http://en.wikipedia.org/wiki/Halting_problem]. i oczywiście możesz kpić countDownLatch, zobacz powermock i whitebox – piotrek