What are the recommended approaches to using Thread.sleep()
to speed up tests.
I am testing a network library with a retry functionality when connection
I would argue why are you trying to test Thread.sleep. It seems to be me you're trying to test the behaviour as a consequence of some event.
i.e. what happens if:
If you model code based on events then you can test what should happen should a particular event occurred rather than having to come up with a construct that masks the concurrent API calls. Else what are you really testing? Are you testing how your application reacts to different stimuli or simply testing the JVM is working correctly?
I agree with the other readers that sometimes it's useful to put an abstraction around any code time or thread related i.e. Virtual clock http://c2.com/cgi/wiki?VirtualClock so you can mock out any timing/concurrent behaviour and concentrate on the behaviour of the unit itself.
It also sounds like you should adopt a state pattern so you object has specific behaviour depending on what state it's in. i.e AwaitingConnectionState, ConnectionDroppedState. Transition to different states would be via the different events i.e. timeout, dropped connection etc. Not sure if this overkill for your needs but it certainly removes a lot of conditional logic which can make the code more complicated and unclear.
If you approach this way, then you can still test behaviour at the unit level whilst still testing in situ with an integration test or acceptance test later.
I just faced a similar issue and I created a Sleeper
interface to abstract this away:
public interface Sleeper
{
void sleep( long millis ) throws InterruptedException;
}
The default implementation uses Thread.sleep()
:
public class ThreadSleeper implements Sleeper
{
@Override
public void sleep( long millis ) throws InterruptedException
{
Thread.sleep( millis );
}
}
In my unit tests, I inject a FixedDateTimeAdvanceSleeper
:
public class FixedDateTimeAdvanceSleeper implements Sleeper
{
@Override
public void sleep( long millis ) throws InterruptedException
{
DateTimeUtils.setCurrentMillisFixed( DateTime.now().getMillis() + millis );
}
}
This allows me to query the time in a unit test:
assertThat( new DateTime( DateTimeUtils.currentTimeMillis() ) ).isEqualTo( new DateTime( "2014-03-27T00:00:30" ) );
Note that you need to fix the time first using DateTimeUtils.setCurrentMillisFixed( new DateTime( "2014-03-26T09:37:13" ).getMillis() );
at the start of your test and restore the time again after the test using DateTimeUtils.setCurrentMillisSystem();
Make the sleeping time configurable through a setter, and provide a default value. So in your unit tests, call the setter with a small argument (1 for example), and then execute the method that would call Thread.sleep()
.
Another similar approach is to make if configurable via a boolean, so that Thread.sleep()
isn't called at all if the boolean
is set to false
.
Eugene is right, make your own component to wrap the system that is out of your control Just done this myself thought I'd share, this is known as 'SelfShunt' check this out:
Generator
is a class that when you call getId()
it returns the current system time.
public class GeneratorTests implements SystemTime {
private Generator cut;
private long currentSystemTime;
@Before
public void setup(){
cut = Generator.getInstance(this);
}
@Test
public void testGetId_returnedUniqueId(){
currentSystemTime = 123;
String id = cut.getId();
assertTrue(id.equals("123"));
}
@Override
public long currentTimeMillis() {
return currentSystemTime;
}
}
We make the test class 'SelfShunt' and become the SystemTime component that way we have full control of what the time is.
public class BlundellSystemTime implements SystemTime {
@Override
public long currentTimeMillis(){
return System.currentTimeMillis();
}
}
We wrap the component that isn't under our control.
public interface SystemTime {
long currentTimeMillis();
}
Then make an interface so our test can 'SelfShunt'
It is usually a good idea to delegate time-related functionality to a separate component. That include getting the current time, as well as delays like Thread.sleep(). This way it is easy to substitute this component with mock during testing, as well as switch to a different implementation.
Create some retry delay type that represents the policy for retry delays. Invoke some method on the policy type for the delay. Mock it as you like. No conditional logic, or true
/false
flags. Just inject the type that you want.
In ConnectRetryPolicy.java
public interface ConnectRetryPolicy {
void doRetryDelay();
}
In SleepConnectRetryPolicy.java
public class final SleepConnectRetryPolicy implements ConnectRetryPolicy {
private final int delay;
public SleepConnectRetryPolicy(final int delay) {
this.delay = delay;
}
@Override
public void doRetryDelay() {
try {
Thread.sleep(delay);
} catch (InterruptedException ie) {
log.error("connection delay sleep interrupted", ie);
}
}
}
In MockConnectRetryPolicy.java
public final class MockConnectRetryPolicy implements ConnectRetryPolicy {
@Override
public void doRetryDelay() {
// no delay
}
}