2013-08-14 9 views
5

Jako praktyk TDD chcę przetestować wszystko, co koduję.Biblioteka do potwierdzania wielu kodów w języku Java

W ciągu ostatnich kilku lat koduję wiele kodów wielowątkowych i jedna część moich testów bardzo przeszkadza.

Kiedy muszę stwierdzić coś, co może się zdarzyć podczas pętli run() I skończyć z pewnym rodzajem o twierdzenie, jak ta:

assertEventually(timeout, assertion) 

wiem, że Mockito ma rozwiązanie tego problemu, ale tylko dla wywołania weryfikuj. Wiem również, że JUnit ma właściwość timeout, która jest przydatna, aby uniknąć powieszenia (lub zawsze trwałych) testów. Ale to, czego chcę, to coś, co pozwala mi stwierdzić coś, co może stać się prawdą w czasie.

Moje pytanie brzmi, czy ktokolwiek wie, że biblioteka zapewnia taką możliwość?

Oto moje rozwiązanie do tej pory:

private void assertEventually(int timeoutInMilliseconds, Runnable assertion){ 
    long begin = System.currentTimeMillis(); 
    long now = begin; 
    Throwable lastException = null; 
    do{ 
     try{ 
      assertion.run(); 
      return; 
     }catch(RuntimeException e){ 
      lastException = e; 
     }catch(AssertionError e){ 
      lastException = e; 
     } 
     now = System.currentTimeMillis(); 
    }while((now - begin) < timeoutInMilliseconds); 
    throw new RuntimeException(lastException); 
} 

Korzystanie kończy się tak:

assertEvetuallyTrue(1000, new Runnable() { 
     public void run() { 
      assertThat(Thread.activeCount()).isEqualTo(before); 
     } 
    }); 
+0

Co stwierdzając w próbce kodu? nazwa metody implikuje stwierdzenie, że coś ma wartość true, ale kod wykonuje tylko Runnable i upewnia się, że nie powoduje błędu. – dkatzel

+0

Zakładam, że użyjesz pewnego rodzaju asercji, aby sprawdzić swoje warunki. Więc rzucają znaczące wyjątki, które pomagają w debugowaniu kodu. Podam przykład. –

+0

Spodziewałbym się, że assertEventually rzuci wyjątek, który wygasa. W twoim przykładzie wygląda na to, że próbuje podać wartość null. Ponadto, może być sens, aby rzucić 'AssertError'. – Gray

Odpowiedz

2

Dwie biblioteki może warto sprawdzić:

  • ConcurrentUnit - pozwala na wykonywanie twierdzeń z dowolnego wątku, czekając ustaloną ilość czasu
  • Awaitility - umożliwia czekać ustaloną ilość czasu lub dopóki nie zostanie spełniony jakiś warunek
+0

Dzięki, ConcurrentUnit robi lewę =] –

1

z języka używanie - twierdząc, że coś może być prawdziwe - to brzmi tak jak idziesz ścieżką, która doprowadzi cię do delikatnych testów. Testy powinny mieć wyniki binarne - albo kończą się lub kończą niepowodzeniem, i nie ma szarej strefy.

Patrząc na tym konkretnym przykładzie, ze względu na wykorzystanie wielowątkowości Proponuję coś takiego:

  • byłaby swój projekt tak, że wszystkie rzeczywiste logiki w kodzie jest łatwo uruchomić niezależne od wielu - środowisko chronione jako synchronicznie wykonywany kod, co powinno sprawiać, że pisanie testów jednostkowych wokół tego jest dość proste.
  • Zaimplementuj zarządzanie wątkami poza całym tym kodem - będzie to znacznie trudniejsze do sprawdzenia przy użyciu testów jednostkowych, dlatego powinieneś zachować czystość odseparowania od reszty kodu, aby nie utrudniać innym kodu test.
  • Rozpocznij tworzenie zestawu testów systemu wysokiego poziomu, który wykonuje system (lub jego duże komponenty) jako całość, prowadząc testy przez zewnętrzne granice aplikacji. Będą one obejmować kod obsługi wątków wraz z testowaniem integracji różnych składników w systemie. Co więcej, nie powinieneś pisać specyficznej logiki testowej, która zajmuje się wątkami - powinny one po prostu działać wewnętrznie w twoim systemie podczas testowania.

Kolejną zaletą dzielenia testów w ten sposób jest to, że tworzy się osobne zestawy testowe do testów jednostkowych i testów systemowych, co powinno pomóc w utrzymaniu pakietu testów jednostkowych szybko i szczuplej (dzięki czemu można go łatwiej uruchomić i częściej w trakcie opracowywania). Wszelkie testy z przekroczeniem limitu czasu (np. Testy systemu w tym przypadku) będą trwały dłużej, a zatem są bardziej odpowiednie do uruchamiania tylko sporadycznie - to podejście ułatwia to zadanie.

+0

Tak naprawdę to już robię. Testuję konkretne zachowanie w prosty sposób. Ale w końcu muszę przetestować, jak rzeczy działają razem, a tego rodzaju twierdzenie ma sens w tej sytuacji. –

+0

Moje stanowisko jest takie, że zastosowanie tego podejścia do testów sprawia, że ​​(a) trudniejsze do testowania i (b) prowadzi do bardziej delikatnych testów. Jeśli przeprowadzisz testy z wyższego poziomu (tj. API ujawnionego przez Twoją aplikację), możesz skonstruować swoje testy w sposób, który nie musi zajmować się tymi problemami.Jednak bardzo bym chciał, aby ktoś mi udowodnił, że to źle, ponieważ może to być przydatna technika, jeśli ktoś może wykazać się dobrym podejściem, które daje bardzo definitywne i niezawodne asercje zaliczeń/niepowodzeń. – robjohncox

+0

Zgadzam się z tobą. Ten rodzaj testu nie powinien być używany do testowania twoich klas z tych samych powodów, które powiedziałeś. Niemniej jednak nadszedł czas, aby wszystkie te rzeczy zebrać razem i trzeba je przetestować. I właśnie wtedy przydaje się takie twierdzenie. –

0

Testowanie na czas jest rodzajem testów wydajności, które, o ile nie masz ciężkich wymagań w czasie rzeczywistym, potwierdzono statystykami wielu próbek.

Testy jednostkowe dotyczyłyby tylko jednej próbki i powinny być deterministyczne. Zamiast tego konstruowania check-and-loop myślę, że powinieneś powiadomić swój wątek JUnit za pomocą jakiegoś mechanizmu, jak tylko pojawi się zdarzenie, na które chcesz wystąpić. Połączenia zwrotne, Futures, zamki, Exchangers itp. Mogą być wykorzystane do zbudowania takich mechanizmów.

Jest to wyzwanie projektowe, aby zbudować swój kod wielowątkowy tak, aby można go było przetestować bez sprawdzania i sprawdzania w pętli.

Ale jeśli naprawdę musisz to zrobić, użyj System.nanoTime() zamiast System.currentTimeMillis(), ponieważ aktualizuje się częściej, chociaż rzeczywista implementacja i dokładność obu zegarów zależy od wersji JVM, systemu operacyjnego i sprzętu.

1

Jeśli pochodzisz ze Scala, możesz być przyzwyczajony do cechy eventually.

Jest to bardzo przydatne do testowania, ponieważ można ponawiać próbę przez pewien czas, aż do momentu, gdy wynik zostanie zwrócony w postaci , w końcu.

Jest takie wrapper Java wykonane przez halfvim tutaj: https://github.com/halfvim/eventually