单元测试在调试模式下成功,但在正常运行时失败。

15
为什么我的单元测试在调试模式下成功,但在正常运行时失败?
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());
  }
}

1
建议尝试将interruptedBitSet声明为volatile - yohlulz
你在调试时设置了断点吗?如果设置了,它在哪里? - Alb
@Alb 我把它放在了 'interruptedBitSet = false;' 上。 - Chris Morris
5个回答

4
很可能是由于调试器中断了主线程但没有中断任何后台线程 - 即ExecutorService中的线程。在Eclipse中进行调试时,您可以将断点更改为中断所有线程而不仅仅是主要线程。
当不进行调试时,任务的提交和立即取消非常快,以至于您在它运行一次之前就取消了该任务。尝试在这些代码行之间添加一个延迟睡眠时间:
final Future<String> future =  pool.submit(new TaskChecksForInterruptedBit());
Thread.sleep(1000);
final boolean wasJustCancelled = future.cancel(true);

为什么在发布模式下,主线程看不到由生成的线程修改的标志? - Chris Morris
让我澄清一下:我将其放置在测试方法中的 'interruptedBitSet = false',而不是 Callable 的 call() 方法中。 - Chris Morris
@Chris-Morris 对不起,我一开始误读了代码,我已经编辑了我的答案并提出了另一个建议。 - Alb

2
我知道这很老套,但我遇到了同样的问题。 我的问题是我正在枚举和检查输出的IEnumerable
在运行单元测试时,IEnumerable返回的排序与调试时不同。 这是IEnumerable的性质,只需添加OrderBy子句即可解决问题。
我希望这能帮助某些人,因为这可能是一个令人沮丧的问题。

1
注意了,reflections.getSubTypesOf(Foo.class) 在运行/调试中的顺序不同。 - yunandtidus

2

你需要确保你的任务已经开始运行。在任务开始之前,它可能会被取消。

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());
    }
}

0
我在进行在线课程的作业时遇到了类似的问题。我将课程中的评分程序添加到构建路径中,它使用了JUnit4,而我的Eclipse版本会为任何新的测试用例添加JUnit5。我创建了一个新的Java项目,并将JUnit5添加到了没有评分程序的测试用例的构建路径中,这解决了我的问题。希望这能帮到你。

0

在终止主线程之前,您应该检查所有线程是否已经结束

private void shutdownExecutionService(ExecutorService executorService) {
    if (executorService != null) {
        try {
            executorService.shutdown();
            while (!executorService.awaitTermination(10, TimeUnit.HOURS)) {
                logger.info("Awaiting completion of threads.");
            }
        } catch (final InterruptedException e) {
            logger.error("Error while shutting down threadpool", e);
        }
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接