Wie kann ich einen JUnit-Test warten lassen?

93

Ich habe einen JUnit-Test, auf den ich synchron einige Zeit warten möchte. Mein JUnit-Test sieht folgendermaßen aus:

@Test
public void testExipres(){
    SomeCacheObject sco = new SomeCacheObject();
    sco.putWithExipration("foo", 1000);
    //WAIT FOR 2 SECONDS
    assertNull(sco.getIfNotExipred("foo"));
}

Ich habe Thread.currentThread (). Wait () ausprobiert, aber es wird eine IllegalMonitorStateException ausgelöst (wie erwartet). Gibt es einen Trick oder brauche ich einen anderen Monitor?

Kylar
quelle

Antworten:

117

Wie wäre es mit Thread.sleep(2000);? :) :)

Muel
quelle
14
Wenn Sie Code-Analyse-Tools wie sonarqube verwenden, beschweren sie sich darüber, Thread.sleepdass die Verwendung von Thread.sleep in einem Test im Allgemeinen nur eine schlechte Idee ist. Es entstehen spröde Tests, die je nach Umgebung ("Passes on my machine!") Oder Belastung unvorhersehbar fehlschlagen können. Verlassen Sie sich nicht auf das Timing (verwenden Sie Mocks) oder verwenden Sie Bibliotheken wie Awaitility für asynchrone Tests.
FuryFart
3
Diese Antwort sollte entfernt und als schädlich angesehen werden. Eine viel bessere Antwort unten ist stackoverflow.com/a/35163873/1229735
yiati
70

Thread.sleep () könnte in den meisten Fällen funktionieren, aber normalerweise warten Sie, wenn Sie warten, tatsächlich auf das Auftreten einer bestimmten Bedingung oder eines bestimmten Zustands. Thread.sleep () garantiert nicht, dass das, worauf Sie warten, tatsächlich passiert ist.

Wenn Sie beispielsweise auf eine Ruhe-Anfrage warten, kehrt diese normalerweise in 5 Sekunden zurück. Wenn Sie jedoch den Ruhezustand für 5 Sekunden an dem Tag einstellen, an dem Ihre Anfrage in 10 Sekunden zurückkommt, schlägt Ihr Test fehl.

Um dies zu beheben, verfügt JayWay über ein großartiges Dienstprogramm namens Awatility, mit dem sichergestellt werden kann, dass ein bestimmter Zustand auftritt, bevor Sie fortfahren .

Es hat auch eine schöne fließende API

await().until(() -> 
{
    return yourConditionIsMet();
});  

https://github.com/jayway/awaitility

Ben Glasser
quelle
Ich konnte es nicht erwarten, in Android Studio zu kompilieren.
IgorGanapolsky
Haben Sie diese Zeile zu Ihrer Gradle-Datei hinzugefügt: compile 'org.awaitility: awaitility: 3.0.0'?
Samoht
Nein. Sie würden dies in einem Testfall hinzufügen, in dem Sie auf eine bestimmte Bedingung warten möchten
Ben Glasser
14

Falls sich Ihr statischer Code-Analysator (wie SonarQube) beschwert, Sie sich aber keinen anderen Weg vorstellen können, als zu schlafen, versuchen Sie es mit einem Hack wie: Awaitility.await().pollDelay(Durations.ONE_SECOND).until(() -> true); Es ist konzeptionell falsch, aber es ist dasselbe wie Thread.sleep(1000).

Der beste Weg ist natürlich, einen Callable mit Ihrer angemessenen Bedingung zu bestehen, anstatt mit der true, die ich habe.

https://github.com/awaitility/awaitility

Rastaman
quelle
@Jitendra: Beachten Sie, dass Awaitility die DurationKlasse früher hatte, aber seit Version 4 wurde sie in umbenannt Durations.
Jacob van Lingen
Eine Einschränkung: Wenn Sie möchten, dass die Abfrageverzögerung 10 Sekunden oder mehr beträgt, müssen Sie das Zeitlimit auf mehr als 10 Sekunden erhöhen, da dies das Standardzeitlimit ist. Die Wartezeit beklagt, dass das Zeitlimit geringer als die Abrufverzögerung ist.
Fokus
12

Sie können die Bibliothek java.util.concurrent.TimeUnit verwenden, die intern Thread.sleep verwendet. Die Syntax sollte folgendermaßen aussehen:

@Test
public void testExipres(){
    SomeCacheObject sco = new SomeCacheObject();
    sco.putWithExipration("foo", 1000);

    TimeUnit.MINUTES.sleep(2);

    assertNull(sco.getIfNotExipred("foo"));
}

Diese Bibliothek bietet eine klarere Interpretation für die Zeiteinheit. Sie können 'STUNDEN' / 'MINUTEN' / 'SEKUNDEN' verwenden.

Akshay Pagar
quelle
Wenn ich einen Arbeitsthread innerhalb eines Tests starte, hat dies sleep()Auswirkungen auf den Arbeitsthread?
Anthony Kong
0

Es gibt ein allgemeines Problem: Es ist schwer, sich über die Zeit lustig zu machen. Es ist auch sehr schlecht, einen Code mit langer Laufzeit / Wartezeit in einen Komponententest einzufügen.

Um eine Planungs-API testbar zu machen, habe ich eine Schnittstelle mit einer realen und einer Scheinimplementierung wie dieser verwendet:

public interface Clock {

    public long getCurrentMillis();

    public void sleep(long millis) throws InterruptedException;

}

public static class SystemClock implements Clock {

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

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

}

public static class MockClock implements Clock {

    private final AtomicLong currentTime = new AtomicLong(0);


    public MockClock() {
        this(System.currentTimeMillis());
    }

    public MockClock(long currentTime) {
        this.currentTime.set(currentTime);
    }


    @Override
    public long getCurrentMillis() {
        return currentTime.addAndGet(5);
    }

    @Override
    public void sleep(long millis) {
        currentTime.addAndGet(millis);
    }

}

Damit können Sie die Zeit in Ihrem Test nachahmen:

@Test
public void testExipres() {
    MockClock clock = new MockClock();
    SomeCacheObject sco = new SomeCacheObject();
    sco.putWithExipration("foo", 1000);
    clock.sleep(2000) // WAIT FOR 2 SECONDS
    assertNull(sco.getIfNotExpired("foo"));
}

Ein fortschrittliches Multithreading-Modell für Clockist natürlich viel komplexer, aber Sie können es beispielsweise mit ThreadLocalReferenzen und einer guten Zeitsynchronisationsstrategie erstellen.

Dávid Horváth
quelle