Skip to content

Commit

Permalink
Intermittent failure was due to Thread.sleep in the code. While perfo…
Browse files Browse the repository at this point in the history
…rming 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.
  • Loading branch information
npathai committed Oct 15, 2018
1 parent db33cc5 commit 829df03
Show file tree
Hide file tree
Showing 3 changed files with 59 additions and 28 deletions.
10 changes: 10 additions & 0 deletions balking/src/main/java/com/iluwatar/balking/DelayProvider.java
Original file line number Diff line number Diff line change
@@ -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);
}
33 changes: 25 additions & 8 deletions balking/src/main/java/com/iluwatar/balking/WashingMachine.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,17 +25,38 @@
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() {
Expand All @@ -56,12 +77,8 @@ public void wash() {
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);
}

/**
Expand Down
44 changes: 24 additions & 20 deletions balking/src/test/java/com/iluwatar/balking/WashingMachineTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -36,32 +33,39 @@
*/
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;
}
}
}

0 comments on commit 829df03

Please sign in to comment.