public final class DelayedRepeater extends Object
Imagine you have a job scheduler you want to test. You add some tasks to this scheduler and after a time period you want to check if all jobs are done:
final List<Job> jobs = new ArrayList<Job>();
final int jobCount = 50;
for (int i = 0; i < jobCount; ++i) {
jobs.add(new Job("Testjob" + i));
}
final Scheduler sut = new Scheduler();
sut.execute(jobs);
Thread.sleep(500);
for (final Job job : jobs) {
assertThat(job.isDone, is(true));
}
The problem with this approach is, that most time on your power machine 500 ms is enough to do the jobs. But sometimes it is not! So this approach will lead to a flaky test which will fail unexpectedly. See Martin Fowlers Blog for more information about non deterministic tests.
This helper class gives you the opportunity to retry tests. This is done by first waiting a time period in
combination with ignoring test failures until the maximum repetition limit is reached. So the above example will looks like:
final List<Job> jobs = new ArrayList<Job>();
final int jobCount = 50;
for (int i = 0; i < jobCount; ++i) {
jobs.add(new Job("Testjob" + i));
}
final Scheduler sut = new Scheduler();
sut.execute(jobs);
DelayedRepeater.create(500, 3).execute(new Runnable() {
public void run() {
for (final Job job : jobs) {
assertThat(job.isDone, is(true));
}
}
});
Or you can use Callable:
final List<Job> jobs = new ArrayList<Job>();
final int jobCount = 50;
for (int i = 0; i < jobCount; ++i) {
jobs.add(new Job("Testjob" + i));
}
final Scheduler sut = new Scheduler();
sut.execute(jobs);
DelayedRepeater.create(500, 3).execute(new Callable<Void>() {
public Void call() throws Exception {
for (final Job job : jobs) {
assertThat(job.isDone, is(true));
}
return null;
}
});
The above example first waits for 500 ms before it invokes the runnable with the assertions. Also it ignores all
test failures if some occurs. Then it waits again 500 ms and ignores failures. This happens
for maximum three times. After that it rethrows the test failures and let your test fail. On
the other hand, if the assertions pass the loop is returned early. So in best case the example will run in 500 ms and
in worst case 1500 ms (plus other execution time).
The great benefit of this approach is that the test will run fast (it loops only until all assertions pass). But it also dynamically retries on a slow machine until a maximum amount of time.
| Modifier and Type | Class and Description |
|---|---|
private static class |
DelayedRepeater.CallableInvoker
Implementation to execute
Callable. |
private static interface |
DelayedRepeater.Invoker
Interface to wrap to invocation of various targets.
|
private static class |
DelayedRepeater.RunnableInvoker
Implementation to execute
Runnable. |
| Modifier and Type | Field and Description |
|---|---|
private int |
currentRetries
Current amount of tried loops.
|
private int |
maxRetries
Maximum amount of retries before failing.
|
private int |
waitMillies
Milliseconds to wait before invoking
Runnable.run(). |
| Modifier | Constructor and Description |
|---|---|
private |
DelayedRepeater(int waitMillies,
int maxRetries)
Dedicated constructor.
|
| Modifier and Type | Method and Description |
|---|---|
static DelayedRepeater |
create(int waitMillies,
int maxRetries)
Factory method to create a new instance.
|
void |
execute(Callable<Void> retriedAssertions)
Takes a
callable with some assertions to execute multiple times. |
private void |
execute(DelayedRepeater.Invoker wrapped)
Executes the wrapped assertions until success or it should fail.
|
void |
execute(Runnable retriedAssertions)
Takes a
runnable with some assertions to execute multiple times. |
(package private) boolean |
shouldFail()
Indicates that the whole test should fail because the maximum retries were exhausted.
|
private final int waitMillies
Runnable.run().private final int maxRetries
private int currentRetries
Initialized and updated by execute(Runnable).
private DelayedRepeater(int waitMillies,
int maxRetries)
Use create(int, int) to get a new instance.
waitMillies - milliseconds to wait before invoking the runnable, must be greater than 0maxRetries - maxim number of tries, must be greater than 0boolean shouldFail()
true if tests should fail, else falsepublic void execute(Runnable retriedAssertions) throws InterruptedException
runnable with some assertions to execute multiple times.retriedAssertions - must not be nullInterruptedException - if the sleep thread is notifiedpublic void execute(Callable<Void> retriedAssertions) throws InterruptedException
callable with some assertions to execute multiple times.retriedAssertions - must not be nullInterruptedException - if the sleep thread is notifiedprivate void execute(DelayedRepeater.Invoker wrapped) throws InterruptedException
wrapped - must not be nullInterruptedException - if the sleep thread is notifiedpublic static DelayedRepeater create(int waitMillies, int maxRetries)
waitMillies - milliseconds to wait before invoking the callback, must be greater than 0maxRetries - maxim number of tries, must be greater than 0null, always new instanceCopyright © 2014 Sven Strittmatter. All Rights Reserved.