Search code examples
javamultithreadingjunitfuturecallable

Unit test succeeds in debug mode but fails when running it normally


Why does my unit test succeed in debug mode but fail when running it normally?

public class ExecutorServiceTest extends MockitoTestCase{   
  private int numThreads;
  private ExecutorService pool;
  private volatile boolean interruptedBitSet;

  @Override
  public void setUp() {
    numThreads = 5;
    pool = Executors.newFixedThreadPool(numThreads);
  }

class TaskChecksForInterruptedBit implements Callable<String> {
    @Override
    public String call() throws Exception {
      interruptedBitSet = false;
      while (!Thread.currentThread().isInterrupted()) {
      }
      interruptedBitSet = Thread.currentThread().isInterrupted();
      return "blah";
    }
  }

public void testCancelSetsInterruptedBitInCallable() throws Exception {
    interruptedBitSet = false;
    final Future<String> future = 
        pool.submit(new TaskChecksForInterruptedBit());
    final boolean wasJustCancelled = future.cancel(true);
    assertTrue(wasJustCancelled);

    // Give time for the thread to notice the interrupted bit and set the flag
    Thread.sleep(5000);

    // This succeeds when stepping through w/ a debugger, but fails when running
    // the test straight. WHY?
    assertTrue(interruptedBitSet);

    assertTrue(future.isDone());
    assertTrue(future.isCancelled());
  }
}

Solution

  • You have to make sure your task actually started running. It may get cancelled before it even has a chance.

    public class ExecutorServiceTest {
        private int numThreads;
        private ExecutorService pool;
        private volatile boolean interruptedBitSet;
        private static final CountDownLatch latch = new CountDownLatch(1);
    
        @Before
        public void setUp() {
            numThreads = 5;
            pool = Executors.newFixedThreadPool(numThreads);
        }
    
        class TaskChecksForInterruptedBit implements Callable<String> {
            @Override
            public String call() throws Exception {
                interruptedBitSet = false;
                latch.countDown();
                while (!Thread.currentThread().isInterrupted()) {
                    System.out.println(System.currentTimeMillis());
                }
                System.out.println("haha");
                interruptedBitSet = Thread.currentThread().isInterrupted();
                return "blah";
            }
        }
    
        @Test
        public void testCancelSetsInterruptedBitInCallable() throws Exception {
            final Future<String> future =
                    pool.submit(new TaskChecksForInterruptedBit());
            interruptedBitSet = false;
            latch.await();
            final boolean wasJustCancelled = future.cancel(true);
            Assert.assertTrue(wasJustCancelled);
    
            // Give time for the thread to notice the interrupted bit and set the flag
            Thread.sleep(5000);
    
            // This succeeds when stepping through w/ a debugger, but fails when running
            // the test straight. WHY?
            Assert.assertTrue(interruptedBitSet);
    
            Assert.assertTrue(future.isDone());
            Assert.assertTrue(future.isCancelled());
        }
    }