Dieser Blog zeigt auf wie man mit einfachsten Mitteln, mit nur einer Java Klasse, diese Stress Tests programmieren kann.
Das folgende Listing zeigt die Klasse ch.std.test.StressTest:
package ch.std.test;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.*;
public class StressTest {
private Callable callable;
private int cycles;
private int runsPerCycle;
private List<Exception> exceptionList;
public StressTest(Callable callable, int cycles, int runsPerCycle) {
this.callable = callable;
this.cycles = cycles;
this.runsPerCycle = runsPerCycle;
this.exceptionList = Collections.synchronizedList(new ArrayList());
}
public void test() throws Exception {
ExecutorService executor = Executors.newFixedThreadPool(this.runsPerCycle);
for (int i = 0; i < this.cycles; i++) {
this.exceptionList.clear();
List<Callable<V>> callables = new ArrayList<>();
for (int j = 0; j < this.runsPerCycle; j++) {
callables.add(callable);
}
List<Future<V>> futures = executor.invokeAll(callables);
for (Future<V> future : futures) {
try {
if (!future.isDone()) {
this.exceptionList.add(new Exception("future is not done"));
} else {
V f = future.get();
}
} catch (CancellationException ce) {
this.exceptionList.add(ce);
} catch (ExecutionException ee) {
this.exceptionList.add(ee);
} catch (InterruptedException ie) {
this.exceptionList.add(ie);
Thread.currentThread().interrupt();
} catch (Exception e) {
this.exceptionList.add(e);
}
}
if (!this.exceptionList.isEmpty()) {
for (Exception e: this.exceptionList) {
System.out.println(e.getMessage());
}
throw new Exception("test with exception, see list");
}
}
}
public void add(Exception e) {
this.exceptionList.add(e);
}
public List<Exception> getExceptionList() {
return Collections.unmodifiableList(exceptionList);
}
public void printResult(PrintStream ps) {
if (this.exceptionList.isEmpty()) {
ps.println("no exceptions");
return;
}
for (Exception exception : exceptionList) {
ps.println(exception.getMessage());
}
}
}
Der 2. Test arbeitet parallel mit dem StressTest. 100 Threads werden via Executor Service und dem fixen ThreadPool gestartet. Alle Threads arbeiten mit dem gleichen Set. Jeder Run entfernt die Zahl, berechnet den Set HashCode und fügt die Zahl wieder ins Set ein. Hier resultiert in der Regel eine ConcurrencyException, oder das Set verfügt über eine falsche Anzahl(size).
Der 3. Test arbeitet analog dem 2. Test, verwendet aber ein Threadsafe Set. Dieser Test funktioniert auch mit dem StressTest einwandfrei.
package ch.std.unittest.demo;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import org.junit.Assert;
import org.junit.Test;
import ch.std.test.StressTest;
public class StressTestDemo {
@Test
public void testSyncSetSingleRun() {
Set iSet = new HashSet<>();
for(int i = 0; i< 100; i++) {
iSet.remove(i);
iSet.add(i);
}
Assert.assertEquals(100, iSet.size());
}
@Test
public void testHashSetMultipleRun() throws Exception {
Set iSet = new HashSet<>();
StressTest stressTest = new StressTest<>(() -> {
for(int i = 0; i< 100; i++) {
iSet.remove(i);// force exception
iSet.hashCode(); // force exception
iSet.add(i);
}
return iSet.size();
}, 1, 100);
stressTest.test();
stressTest.printResult(System.out);
Assert.assertEquals(100, iSet.size());
Assert.assertTrue(stressTest.getExceptionList().isEmpty());
}
@Test
public void testConcurrentHashSetMultipleRun() throws Exception {
Set iSet = Collections.synchronizedSet(new HashSet());
StressTest stressTest = new StressTest<>(() -> {
for(int i = 0; i< 100; i++) {
iSet.remove(i);
iSet.hashCode();
iSet.add(i);
}
return iSet.size();
}, 1, 100);
stressTest.test();
stressTest.printResult(System.out);
Assert.assertEquals(100, iSet.size());
Assert.assertTrue(stressTest.getExceptionList().isEmpty());
}
}
Der StressTest zeigt auf, dass die HashSet Klasse nicht threadsafe ist.
Es ist in jedem Projekt zentral, dass relevante Codeteile auf ihre korrekt parallele Ausführung getestet werden. Insbesondere Server Komponenten wie Web oder REST Services sind hier besonders zu berücksichtigen.
Die StressTest Klasse erleichtert uns hier die Arbeit.