
Sam
Wie testet man mit JUnit Methoden, die asynchrone Prozesse auslösen?
Ich weiß nicht, wie ich meinen Test auf das Ende des Prozesses warten lassen soll (es ist nicht gerade ein Komponententest, sondern eher ein Integrationstest, da er mehrere Klassen und nicht nur eine umfasst).

Martin
TL;DR; Leider gibt es keine eingebaute Lösung noch (zum Zeitpunkt des Schreibens 2022), daher steht es Ihnen frei, alles zu verwenden und / oder zu implementieren, was zu Ihrer Situation passt.
Beispiel
Eine Alternative ist die Verwendung von CountDownLatch Klasse.
public class DatabaseTest {
/**
* Data limit
*/
private static final int DATA_LIMIT = 5;
/**
* Countdown latch
*/
private CountDownLatch lock = new CountDownLatch(1);
/**
* Received data
*/
private List<Data> receiveddata;
@Test
public void testDataRetrieval() throws Exception {
Database db = new MockDatabaseImpl();
db.getData(DATA_LIMIT, new DataCallback() {
@Override
public void onSuccess(List<Data> data) {
receiveddata = data;
lock.countDown();
}
});
lock.await(2000, TimeUnit.MILLISECONDS);
assertNotNull(receiveddata);
assertEquals(DATA_LIMIT, receiveddata.size());
}
}
HINWEIS Sie können nicht einfach verwendet werden synchronisiert mit einem regulären Objekt als Sperre, da schnelle Rückrufe die Sperre freigeben können, bevor die Wait-Methode der Sperre aufgerufen wird. Sehen Dies Blogbeitrag von Joe Walnes.
BEARBEITEN Dank der Kommentare von @jtahlborn und @Ring wurden synchronisierte Blöcke um CountDownLatch entfernt

Johann
Sie können versuchen, die zu verwenden Erwartung Bibliothek. Es macht es einfach, die Systeme zu testen, von denen Sie sprechen.

Matt Sgarlata
Wenn Sie eine verwenden ErgänzbarZukunft (eingeführt in Java 8) oder a SettableFuture (aus Google Guave), können Sie Ihren Test sofort beenden, anstatt eine voreingestellte Zeit zu warten. Ihr Test würde in etwa so aussehen:
CompletableFuture<String> future = new CompletableFuture<>();
executorService.submit(new Runnable() {
@Override
public void run() {
future.complete("Hello World!");
}
});
assertEquals("Hello World!", future.get());
Notiz dass es eine Bibliothek gibt, die bietet CompletableFuture
für Prä-Java-8, das sogar dieselben Namen verwendet (und alle zugehörigen Java-8-Klassen bereitstellt), wie:
net.sourceforge.streamsupport:streamsupport-minifuture:1.7.4
Dies ist nützlich für die Android-Entwicklung, wo wir Codes mit Geräten vor Android-7 kompatibel halten wollen, selbst wenn wir mit JDK-v11 bauen.
IMHO ist es eine schlechte Praxis, Unit-Tests zu erstellen oder auf Threads zu warten usw. Sie möchten, dass diese Tests in Sekundenbruchteilen ausgeführt werden. Aus diesem Grund möchte ich einen zweistufigen Ansatz zum Testen asynchroner Prozesse vorschlagen.
- Testen Sie, ob Ihr asynchroner Prozess ordnungsgemäß übermittelt wird. Sie können das Objekt verspotten, das Ihre asynchronen Anfragen akzeptiert, und sicherstellen, dass der übermittelte Job die richtigen Eigenschaften hat usw.
- Testen Sie, ob Ihre asynchronen Rückrufe die richtigen Dinge tun. Hier können Sie den ursprünglich gesendeten Job nachahmen und davon ausgehen, dass er ordnungsgemäß initialisiert wurde, und überprüfen, ob Ihre Rückrufe korrekt sind.
Starten Sie den Vorgang und warten Sie mit a auf das Ergebnis Future
.
Eine Methode, die ich zum Testen asynchroner Methoden als ziemlich nützlich empfunden habe, ist das Einfügen von an Executor
-Instanz im Konstruktor des zu testenden Objekts. In der Produktion ist die Executor-Instanz so konfiguriert, dass sie asynchron ausgeführt wird, während sie im Test so simuliert werden kann, dass sie synchron ausgeführt wird.
Angenommen, ich versuche, die asynchrone Methode zu testen Foo#doAsync(Callback c)
,
class Foo {
private final Executor executor;
public Foo(Executor executor) {
this.executor = executor;
}
public void doAsync(Callback c) {
executor.execute(new Runnable() {
@Override public void run() {
// Do stuff here
c.onComplete(data);
}
});
}
}
In der Produktion würde ich konstruieren Foo
mit einem Executors.newSingleThreadExecutor()
Executor-Instanz während des Tests Ich würde sie wahrscheinlich mit einem synchronen Executor erstellen, der Folgendes tut –
class SynchronousExecutor implements Executor {
@Override public void execute(Runnable r) {
r.run();
}
}
Jetzt ist mein JUnit-Test der asynchronen Methode ziemlich sauber —
@Test public void testDoAsync() {
Executor executor = new SynchronousExecutor();
Foo objectToTest = new Foo(executor);
Callback callback = mock(Callback.class);
objectToTest.doAsync(callback);
// Verify that Callback#onComplete was called using Mockito.
verify(callback).onComplete(any(Data.class));
// Assert that we got back the data that we expected.
assertEquals(expectedData, callback.getData());
}
Es ist grundsätzlich nichts falsch daran, Threaded/Async-Code zu testen, insbesondere wenn Threading dies ist Der Punkt des Codes, den Sie testen. Der allgemeine Ansatz zum Testen dieses Zeugs ist:
- Blockieren Sie den Haupttest-Thread
- Erfassen Sie fehlgeschlagene Behauptungen von anderen Threads
- Entsperren Sie den Haupttest-Thread
- Werfen Sie alle Fehler erneut aus
Aber das ist eine Menge Boilerplate für einen Test. Ein besserer/einfacherer Ansatz ist einfach zu verwenden ConcurrentUnit:
final Waiter waiter = new Waiter();
new Thread(() -> {
doSomeWork();
waiter.assertTrue(true);
waiter.resume();
}).start();
// Wait for resume() to be called
waiter.await(1000);
Der Vorteil davon gegenüber der CountdownLatch
Ansatz ist, dass es weniger ausführlich ist, da Behauptungsfehler, die in einem Thread auftreten, ordnungsgemäß an den Haupt-Thread gemeldet werden, was bedeutet, dass der Test fehlschlägt, wenn er sollte. Eine Beschreibung, die die vergleicht CountdownLatch
Ansatz für ConcurrentUnit ist hier.
Ich habe auch eine geschrieben Blogeintrag zum Thema für diejenigen, die etwas mehr Details erfahren möchten.
10160400cookie-checkVerwendung von JUnit zum Testen asynchroner Prozesseyes
Sie könnten JAT (Java Asynchronous Test) ausprobieren: bitbucket.org/csolar/jat
– cs0lar
19. Januar 2013 um 15:20 Uhr
JAT hat 1 Beobachter und wurde seit 1,5 Jahren nicht aktualisiert. Awaitility wurde erst vor einem Monat aktualisiert und befindet sich zum Zeitpunkt der Erstellung dieses Artikels auf Version 1.6. Ich bin mit keinem der beiden Projekte verbunden, aber wenn ich in eine Erweiterung meines Projekts investieren würde, würde ich Awaitility zu diesem Zeitpunkt mehr Glauben schenken.
– Les Hazlewood
3. September 2014 um 20:14 Uhr
JAT hat noch keine Updates: “Last updated 2013-01-19”. Sparen Sie sich einfach die Zeit, um dem Link zu folgen.
– Dämon
17. Oktober 2017 um 8:25 Uhr
@LesHazlewood, ein Beobachter ist schlecht für JAT, aber seit Jahren keine Updates … Nur ein Beispiel. Wie oft aktualisieren Sie den Low-Level-TCP-Stack Ihres Betriebssystems, wenn es nur funktioniert? Eine Alternative zu JAT wird unter stackoverflow.com/questions/631598/… beantwortet.
– Benutzer1742529
6. August 2019 um 8:36 Uhr