2009-12-08 13 views
8

Jakie są zalecane podejścia do korzystania z Thread.sleep() w celu przyspieszenia testów.Testowanie za pomocą Thread.sleep

Testuję bibliotekę sieciową z funkcją ponawiania po zerwaniu połączeń lub wystąpieniu błędów przekroczenia limitu czasu itp. Jednak biblioteka używa Thread.sleep() między kolejnymi próbami (aby nie nawiązywać połączenia tysiące razy podczas restartowania serwera) . Połączenie znacznie spowalnia testy jednostki i zastanawiam się, jakie są opcje, aby je zastąpić.

Uwaga, jestem otwarty na faktyczną zmianę kodu lub za pomocą szyderczego schematu, aby sfałszować Thread.sleep(), ale chciałbym najpierw usłyszeć twoje opinie/rekomendacje.

+0

to może być postrzegane jako casestudy do testowania jednostkowego z kpiną – dfa

+0

Nie używaj Thread.sleep(), użyj metody wait(). Thread.sleep() używa cykli procesora, natomiast wait() nie. – dhiller

Odpowiedz

11

Zwykle jest to dobry pomysł, aby przekazać funkcji związanych z czasem do oddzielny składnik. Obejmuje to uzyskanie bieżącego czasu, a także opóźnień, takich jak Thread.sleep(). W ten sposób łatwo jest zastąpić ten komponent próbą podczas testowania, a także przejść do innej implementacji.

+1

Kusiło mnie, aby stworzyć klasę "Maszynową", która mogłaby to wszystko wykreślić. – notnoop

+0

Droga do pracy. W ten sposób możesz również ustawić swoją konfigurację lub nawet pominąć wszystko, co chcesz, w jednym miejscu, a następnie rozłożyć tę logikę przez kod. –

+0

@notnoop Dokładnie to, co zrobiłem z klasą 'Clock'. Obsługuje podstawowe operacje, takie jak 'Clock.now()', 'Clock.freeze()', 'Clock.freeze (pointInTime)' i tak dalej. Żaden komponent nie współdziała bezpośrednio ze środowiskiem wykonawczym, aby pobrać czas, ale raczej przechodzi przez 'Zegar'. –

3

Dostosuj czas snu do ustawień za pomocą osoby ustawiającej i podaj wartość domyślną. Tak więc w twoich testach jednostkowych wywołaj setera za pomocą małego argumentu (na przykład 1), a następnie wykonaj metodę, która będzie wywoływać Thread.sleep().

Innym podobnym podejściem jest wykonanie, jeśli można go skonfigurować za pomocą wartości logicznej, tak aby Thread.sleep() nie było w ogóle wywoływane, jeśli boolean jest ustawione na false.

2

Utwórz typ opóźnionego opóźnienia, który reprezentuje zasadę opóźnień ponownych prób. Wywołaj metodę dla typu polityki dla opóźnienia. Kpić tak, jak chcesz. Brak logiki warunkowej lub flagi. Po prostu wpisz odpowiedni typ.

W ConnectRetryPolicy.java

public interface ConnectRetryPolicy { 
    void doRetryDelay(); 
} 

W SleepConnectRetryPolicy.java

public class final SleepConnectRetryPolicy implements ConnectRetryPolicy { 
    private final int delay; 
    public SleepConnectRetryPolicy(final int delay) { 
     this.delay = delay; 
    } 

    @Override 
    public void doRetryDelay() { 
     try { 
      Thread.sleep(delay); 
     } catch (InterruptedException ie) { 
      log.error("connection delay sleep interrupted", ie); 
     } 
    } 
} 

W MockConnectRetryPolicy.java

public final class MockConnectRetryPolicy implements ConnectRetryPolicy {  
    @Override 
    public void doRetryDelay() { 
     // no delay 
    } 
} 
+0

To nie tylko polityka, ale konfigurowalny executor snu. Polityka reprezentowałaby tylko strategię wokół snu, a nie jej wykonywania. Dla reszty, zdrowy pomysł. –

1

Eugene ma rację, zrobić własny komponent owinąć system, który jest poza kontrolą tylko zrobić to sam myślałem, że będę dzielić ten jest znany jako „SelfShunt” to sprawdź:

Generator jest klasa, która wywołując getId() zwraca bieżący czas systemowy.

public class GeneratorTests implements SystemTime { 

    private Generator cut; 
    private long currentSystemTime; 

    @Before 
    public void setup(){ 
     cut = Generator.getInstance(this); 
    } 

    @Test 
    public void testGetId_returnedUniqueId(){ 
     currentSystemTime = 123; 

     String id = cut.getId(); 

     assertTrue(id.equals("123")); 
    } 

    @Override 
    public long currentTimeMillis() { 
     return currentSystemTime; 
    } 
} 

Wykonujemy klasę testową „SelfShunt” i stać się składnikiem SYSTEMTIME ten sposób mamy pełną kontrolę tego, co jest czas.

public class BlundellSystemTime implements SystemTime { 

    @Override 
    public long currentTimeMillis(){ 
     return System.currentTimeMillis(); 
    } 
} 

Zawijamy komponent, który nie jest pod naszą kontrolą.

public interface SystemTime { 

    long currentTimeMillis(); 

} 

Następnie uczynić interfejs więc nasz test może 'SelfShunt'

1

Uważam, dlaczego starasz się przetestować Thread.Sleep. Wygląda na to, że próbujesz przetestować zachowanie w wyniku jakiegoś zdarzenia.

tj.co się stanie, jeśli:

  • limit czasu połączenia
  • połączenie spadła

Jeśli model kodu w oparciu o zdarzenia, to możesz sprawdzić, co powinno się zdarzyć powinny szczególne wydarzenie nastąpiło zamiast wymyślić konstrukt, który maskuje równoczesne wywołania API. Inaczej, co naprawdę testujesz? Czy testujesz, w jaki sposób twoja aplikacja reaguje na różne bodźce lub po prostu sprawdza, czy JVM działa poprawnie?

Zgadzam się z innymi czytelnikami, że czasami przydatne jest umieszczenie abstrakcji wokół dowolnego kodu czasowego lub wątku związanego np. Z zegarem wirtualnym http://c2.com/cgi/wiki?VirtualClock, aby można było wyłudzić zachowanie czasowe/równoczesne i skoncentrować się na zachowaniu samej jednostki.

Wygląda również na to, że powinieneś przyjąć wzorzec stanu, aby obiekt zachowywał się specyficznie w zależności od stanu, w jakim się znajduje. Np. OczekiwanieConnectionState, ConnectionDroppedState. Przejście do różnych stanów odbywałoby się za pośrednictwem różnych zdarzeń, np. Przekroczenia limitu czasu, zerwania połączenia itp. Nie jestem pewien, czy to przesada dla twoich potrzeb, ale z pewnością usuwa wiele logicznych warunków, które mogą sprawić, że kod będzie bardziej skomplikowany i niejasny.

Jeśli podchodzisz w ten sposób, możesz nadal testować zachowanie na poziomie jednostki, nadal testując na miejscu z późniejszym testem integracyjnym lub testem akceptacyjnym.

2

Właśnie w obliczu podobnego problemu i ja stworzyliśmy interfejs Sleeper abstrahować to stąd:

public interface Sleeper 
{ 
    void sleep(long millis) throws InterruptedException; 
} 

Domyślna implementacja wykorzystuje Thread.sleep():

public class ThreadSleeper implements Sleeper 
{ 
    @Override 
    public void sleep(long millis) throws InterruptedException 
    { 
     Thread.sleep(millis); 
    } 
} 

W moich testów jednostkowych, ja wstrzyknąć FixedDateTimeAdvanceSleeper :

public class FixedDateTimeAdvanceSleeper implements Sleeper 
{ 
    @Override 
    public void sleep(long millis) throws InterruptedException 
    { 
     DateTimeUtils.setCurrentMillisFixed(DateTime.now().getMillis() + millis); 
    } 
} 

To pozwala mi O kwerendy czasu w badanej jednostki:

assertThat(new DateTime(DateTimeUtils.currentTimeMillis())).isEqualTo(new DateTime("2014-03-27T00:00:30")); 

Należy pamiętać, że trzeba naprawić po raz pierwszy za pomocą DateTimeUtils.setCurrentMillisFixed(new DateTime("2014-03-26T09:37:13").getMillis()); na początku testu i przywrócić po raz po teście z użyciem DateTimeUtils.setCurrentMillisSystem();

Powiązane problemy