Intermittent failure was due to Thread.sleep in the code. While performing unit test cases there was race condition between two threads, so it was not guaranteed to work every time. Used an interface DelayProvider for simulating delay, and while unit testing fake delay provider is used that eradicates the use of Threads in unit test cases, which is not a good practice.

This commit is contained in:
Narendra Pathai 2018-10-15 16:36:27 +05:30
parent db33cc533b
commit 829df031c3
3 changed files with 59 additions and 28 deletions

View File

@ -0,0 +1,10 @@
package com.iluwatar.balking;
import java.util.concurrent.TimeUnit;
/**
* An interface to simulate delay while executing some work.
*/
public interface DelayProvider {
void executeAfterDelay(long interval, TimeUnit timeUnit, Runnable task);
}

View File

@ -25,17 +25,38 @@ package com.iluwatar.balking;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.concurrent.TimeUnit;
/**
* Washing machine class
*/
public class WashingMachine {
private static final Logger LOGGER = LoggerFactory.getLogger(WashingMachine.class);
private final DelayProvider delayProvider;
private WashingMachineState washingMachineState;
/**
* Creates a new instance of WashingMachine
*/
public WashingMachine() {
washingMachineState = WashingMachineState.ENABLED;
this((interval, timeUnit, task) -> {
try {
Thread.sleep(timeUnit.toMillis(interval));
} catch (InterruptedException ie) {
ie.printStackTrace();
}
task.run();
});
}
/**
* Creates a new instance of WashingMachine using provided delayProvider. This constructor is used only for
* unit testing purposes.
*/
public WashingMachine(DelayProvider delayProvider) {
this.delayProvider = delayProvider;
this.washingMachineState = WashingMachineState.ENABLED;
}
public WashingMachineState getWashingMachineState() {
@ -56,12 +77,8 @@ public class WashingMachine {
washingMachineState = WashingMachineState.WASHING;
}
LOGGER.info("{}: Doing the washing", Thread.currentThread().getName());
try {
Thread.sleep(50);
} catch (InterruptedException ie) {
ie.printStackTrace();
}
endOfWashing();
this.delayProvider.executeAfterDelay(50, TimeUnit.MILLISECONDS, this::endOfWashing);
}
/**

View File

@ -22,11 +22,8 @@
*/
package com.iluwatar.balking;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import static org.junit.jupiter.api.Assertions.assertEquals;
@ -36,32 +33,39 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
*/
public class WashingMachineTest {
private volatile WashingMachineState machineStateGlobal;
private FakeDelayProvider fakeDelayProvider = new FakeDelayProvider();
@Disabled
@Test
public void wash() throws Exception {
WashingMachine washingMachine = new WashingMachine();
ExecutorService executorService = Executors.newFixedThreadPool(2);
executorService.execute(washingMachine::wash);
executorService.execute(() -> {
washingMachine.wash();
machineStateGlobal = washingMachine.getWashingMachineState();
});
executorService.shutdown();
try {
executorService.awaitTermination(10, TimeUnit.SECONDS);
} catch (InterruptedException ie) {
ie.printStackTrace();
}
public void wash() {
WashingMachine washingMachine = new WashingMachine(fakeDelayProvider);
washingMachine.wash();
washingMachine.wash();
WashingMachineState machineStateGlobal = washingMachine.getWashingMachineState();
fakeDelayProvider.task.run();
// washing machine remains in washing state
assertEquals(WashingMachineState.WASHING, machineStateGlobal);
// washing machine goes back to enabled state
assertEquals(WashingMachineState.ENABLED, washingMachine.getWashingMachineState());
}
@Test
public void endOfWashing() throws Exception {
public void endOfWashing() {
WashingMachine washingMachine = new WashingMachine();
washingMachine.wash();
assertEquals(WashingMachineState.ENABLED, washingMachine.getWashingMachineState());
}
private class FakeDelayProvider implements DelayProvider {
private Runnable task;
@Override
public void executeAfterDelay(long interval, TimeUnit timeUnit, Runnable task) {
this.task = task;
}
}
}