Trenn-Concurrency für Ihre Klasse
Testing Sachen gleichzeitig ist hart (tm)! GOOS empfiehlt unter anderem, den Nebenläufigkeitsteil von den Teilen zu trennen, die etwas Arbeit machen. Also zum Beispiel, wenn Sie eine Scheduler
hatten, die eine Aufgabe auf einem oder mehreren Threads planen soll. Sie können den Teil, der für das Threading zuständig ist, an Ihren Scheduler übergeben und einfach testen, ob der Scheduler korrekt mit diesem Objekt zusammenarbeitet. Dies ist mehr in einem klassischen Unit-Test-Stil.
Ein Beispiel mit einem `Scheduler ist here, dies verwendet ein Mock Framework, um zu helfen. Wenn Sie mit diesen Ideen nicht vertraut sind, keine Sorge, sie sind wahrscheinlich nicht relevant für Ihren Test.
Nachdem dies gesagt wurde, möchten Sie möglicherweise Ihre Klasse 'im Kontext' in einer Multi-Threading-Weise ausführen. Dies scheint die Art von Test zu sein, den Sie oben schreiben. Der Trick dabei ist, den Test deterministisch zu halten. Nun, ich sage das, Theres ein paar Möglichkeiten.
deterministische
Wenn können Sie Setup Ihren Test in einer deterministischen Art und Weise, um die Fortschritte, für die Bedingungen an den wichtigen Punkten warten, bevor er nach vorn zu erfüllen, können Sie versuchen, eine bestimmte Bedingung zu simulieren zu testen. Das bedeutet, dass Sie genau verstehen, was Sie testen möchten (z. B. den Code in einen Deadlock zwingen) und deterministisch vorgehen (z. B. mit Abstraktionen wie CountdownLatches
usw., um die beweglichen Teile zu "synchronisieren").
Wenn Sie versuchen, einen Multithread-Test zum Synchronisieren der sich bewegenden Teile durchzuführen, können Sie die Ihnen zur Verfügung stehende Concurrency-Abstraktion verwenden, aber es ist schwierig, weil es gleichzeitig ist; Dinge könnten in einer unerwarteten Reihenfolge passieren. Sie versuchen, dies in Ihrem Test mithilfe der sleep
Anrufe mitegate. Wir schlafen normalerweise nicht gerne in einem Test, weil es den Test langsamer laufen lässt und wenn Tausende von Tests ausgeführt werden, zählt jede Minute. Wenn Sie den Schlafzeitraum zu sehr verringern, wird der Test nicht deterministisch und die Reihenfolge ist nicht garantiert.
Einige Beispiele hierfür sind
Sie haben eine der gotchas gesichtet, wo der Haupttest Thread vor den neu gelaicht Fäden im Test abschließen wird abgeschlossen (mit der join
). Eine andere Möglichkeit besteht darin, auf eine Bedingung zu warten, z. B. mit WaitFor.
Soak/Load Testing
Eine andere Wahl Setup ist ein Test einrichten, ausführen und Spam-Klassen in einem Versuch, sie zu überlasten und sie zu zwingen, einige subtile Concurrency Problem zu verraten. Hier müssen Sie genau wie im anderen Stil eine bestimmte Assertion einrichten, damit Sie erkennen können, ob und wann die Klassen sich selbst verraten haben.
Für Ihren Test dann, würde ich vorschlagen, eine Behauptung zu kommen, so dass Sie positive und negative Läufe gegen Ihre Klasse sehen und die sleep
(und system.out
Anrufe ersetzen können. Wenn Sie können, führen Sie Ihren Test aus so etwas wie JUnit ist eigenwillig.
zum Beispiel kann ein Grund Test in der Art, die Sie begonnen haben, nach unten wie diese
public class TestDriver {
private static final CyclicBarrier barrier = new CyclicBarrier(3);
private static final AtomicInteger counter = new AtomicInteger(0);
static class Runnable1 implements Runnable {
public void run() {
try {
barrier.await();
counter.getAndIncrement();
} catch (Exception ie) {
throw new RuntimeException();
}
}
}
@Test (timeout = 200)
public void shouldContinueAfterBarrier() throws InterruptedException {
Thread t1 = new Thread(new Runnable1());
Thread t2 = new Thread(new Runnable1());
Thread t3 = new Thread(new Runnable1());
t1.start();
t2.start();
t3.start();
t1.join();
t2.join();
t3.join();
assertThat(counter.get(), is(3));
}
}
Wenn möglich aussehen könnte, das Hinzufügen einer Zeitüberschreitung zu Ihrem Barrier ist eine gute Übung und würde helfen, einen negativen Test zu schreiben wie dieser
public class TestDriver {
private static final CyclicBarrier barrier = new CyclicBarrier(3);
private static final AtomicInteger counter = new AtomicInteger(0);
static class Runnable1 implements Runnable {
public void run() {
try {
barrier.await(10, MILLISECONDS);
counter.getAndIncrement();
} catch (Exception ie) {
throw new RuntimeException();
}
}
}
@Test (timeout = 200)
public void shouldTimeoutIfLastBarrierNotReached() throws InterruptedException {
Thread t1 = new Thread(new Runnable1());
Thread t2 = new Thread(new Runnable1());
t1.start();
t2.start();
t1.join();
t2.join();
assertThat(counter.get(), is(not((3))));
}
}
Wenn Sie Ihre Implementierung veröffentlichen möchten, können wir möglicherweise mehr Alternativen vorschlagen. Ich hoffe, dass, obwohl Ihnen einige Ideen gibt ...
EDIT: Ein andere Wahl in dem Sperrobjekt für feinkörnige Behauptung zu erreichen, ist zum Beispiel
@Test (timeout = 200)
public void shouldContinueAfterBarrier() throws InterruptedException, TimeoutException {
Thread t1 = new Thread(new BarrierThread(barrier));
Thread t2 = new Thread(new BarrierThread(barrier));
Thread t3 = new Thread(new BarrierThread(barrier));
assertThat(barrier.getNumberWaiting(), is(0));
t1.start();
t2.start();
waitForBarrier(2);
t3.start();
waitForBarrier(0);
}
private static void waitForBarrier(final int barrierCount) throws InterruptedException, TimeoutException {
waitOrTimeout(new Condition() {
@Override
public boolean isSatisfied() {
return barrier.getNumberWaiting() == barrierCount;
}
}, timeout(millis(500)));
}
EDIT: ich http://tempusfugitlibrary.org/recipes/2012/05/20/testing-concurrent-code/ einige dieses oben geschrieben
es ist ziemlich ausführlich, aber die einzige Sache, die ich erwarten würde zu sehen ist etwas, das die LockBarrier benachrichtigt, damit ein anderer Thread fortfahren kann. Wenn Sie etwas wie die eingebaute Condition-Klasse tun, könnten Sie erwarten, dass ein Signal() aufgerufen wird. –
signalAll() wird innerhalb der awarn() - Implementierung der LockBarrier ausgeführt. Wenn alle Fäden die Barriere erreicht haben, signalisieren Sie uns alles. Was würden Sie vorschlagen, es weniger wortreich zu machen? –
Wie war meine Antwort unten? War es nützlich? – Toby