如何有效地测试这段代码?

时间:2015-04-15 16:40:13

标签: java multithreading unit-testing junit concurrency

我为抛弃这个随机主题道歉,但我没有提出更好的名字,

class ReportSenderRunnable implements Runnable {

    private final LPLogCompressor compressor;

    public ReportSenderRunnable(final LPLogCompressor compressor) {
      this.compressor = compressor;
    }

    @Override
    public void run() {
      executeTasks();
    }

    private void executeTasks() {
      try {
//        compressor.compress();
        reportStatus = ReportStatus.COMPRESSING;
        System.out.println("compressing for 10 seconds");
        Thread.sleep(10000);
      } catch (final IllegalStateException e) {
        logCompressionError(e.getMessage());
      } /*catch (final IOException e) {
        logCompressionError(e.getMessage());
      }*/ catch (InterruptedException e) {
        logCompressionError(e.getMessage());
      }

      try {
        reportStatus = ReportStatus.SENDING;
        System.out.println("sending for 10 seconds");
        Thread.sleep(10000);
      } catch (final InterruptedException e) {
        reportStatus = ReportStatus.EXCEPTION_IN_SENDING;
      }

      try {
        reportStatus = ReportStatus.SUBMITTING_REPORT;
        System.out.println("submitting report for 10 seconds");
        Thread.sleep(10000);
      } catch (final InterruptedException e) {
        reportStatus = ReportStatus.EXCEPTION_IN_SUBMITTING_REPORT;
      }
      System.out.println("Report Sender completed");
      reportStatus = ReportStatus.DONE;
    }

    private void logCompressionError(final String cause) {
      logError(ReportStatus.COMPRESSING, cause);
      reportStatus = ReportStatus.EXCEPTION_IN_COMPRESSION;
    }

    private void logError(final ReportStatus status, final String cause) {
      LOGGER.error("{} - {}", status, cause);
    }
  }

理想情况下,如

之类的陈述
System.out.println("sending for 10 seconds");
Thread.sleep(10000);

将被实际任务所取代,但现在假设是这种情况,并且它们的运行方式是

  private void submitJob() {
    final ExecutorService executorService = Executors.newSingleThreadExecutor();
    try {
      final LPLogCompressor lpLogCompressor = getLpLogCompressor();
      executorService.execute(getReportSenderRunnable(lpLogCompressor));
    } catch (final IOException e) {
      reportStatus = ReportStatus.EXCEPTION_IN_COMPRESSION;
      LOGGER.debug("Error in starting compression: {}", e.getMessage());
    }
    System.out.println("started Report Sender Job");
  }

我的问题是如何有效地测试这段代码?我写的那个是

 @Test
  public void testJobAllStages() throws InterruptedException, IOException {
    final ReportSender reportSender = spy(new ReportSender());
    doReturn(compressor).when(reportSender).getLpLogCompressor();
    when(compressor.compress()).thenReturn("nothing");
    reportSender.sendAndReturnStatus();
    Thread.sleep(10);
    assertEquals(ReportStatus.COMPRESSING, reportSender.getCurrentStatus());
    Thread.sleep(10000);
    assertEquals(ReportStatus.SENDING, reportSender.getCurrentStatus());
    Thread.sleep(10000);
    assertEquals(ReportStatus.SUBMITTING_REPORT, reportSender.getCurrentStatus());
  }

这适用于上面的代码。 对我来说,由于以下原因,这很糟糕

  1. 在理想情况下,并非所有任务都需要相同的时间
  2. 使用Thread.sleep进行测试会花费太多时间并且还会增加非确定性。
  3. 问题

    1. 如何有效地测试?

2 个答案:

答案 0 :(得分:0)

您可以使用接受Callable的方法(例如,TimedAssertion.waitForCallable)添加一个类,然后使用ExecutorService每秒执行一次Callable,直到它返回true。如果它在特定时间内没有返回true,则失败。

然后你会从你的测试中调用该类:

boolean result;
result = new TimedAssertion().waitForCallable(() -> 
    reportSender.getCurrentStatus() == ReportStatus.COMPRESSING);
assertTrue(result);
result = new TimedAssertion().waitForCallable(() -> 
    reportSender.getCurrentStatus() == ReportStatus.SENDING);
assertTrue(result);

...等。这样,您可以轻松地等待代码中的特定状态为真,而无需等待太长时间 - 您可以在需要此类断言的任何地方重用此新类。

答案 1 :(得分:0)

基于@Boris the Spider评论,我使用了模拟,这是我的测试看起来像

  @Mock
  private ReportSenderRunnable reportSenderRunnable;

  @Mock
  private LPLogCompressor compressor;

  @Before
  public void setUp() throws Exception {
    MockitoAnnotations.initMocks(this);
  }

  @Test(timeout = 1000)
  public void testJobNoException() throws InterruptedException, IOException {
    final ReportSender reportSender = spy(new ReportSender());
    doReturn(compressor).when(reportSender).getLpLogCompressor();
    when(compressor.compress()).thenReturn("nothing");
    reportSender.sendAndReturnStatus();
    Thread.sleep(10);
    assertEquals("Job must be completed successfully", ReportStatus.DONE,
                 reportSender.getCurrentStatus());
  }

  @Test(timeout = 1000)
  public void testJobWithIllegalStateException() throws Exception {
    final ReportSender reportSender = spy(new ReportSender());
    doReturn(compressor).when(reportSender).getLpLogCompressor();
    doThrow(IllegalStateException.class).when(compressor).compress();
    reportSender.sendAndReturnStatus();
    Thread.sleep(10);
    assertEquals("Job must failed during compression", ReportStatus.EXCEPTION_IN_COMPRESSION,
                 reportSender.getCurrentStatus());
  }