Асинхронный тест wihout Thread.sleep

В настоящее время я хочу проверить, правильно ли отвечает SUT (System under test) запросы на внешние системы. Я использую внешние mocks (через HTTP) и очереди сообщений в среде QA (они могут быть обменены реальными системами через конфигурацию).

Проблема: делать утверждения с этими внешними системами, я распространяю thread.sleep() над тестами интеграции, и это плохо. Я хочу заменить это на запрос с обратным вызовом и набором тестов, который предоставляет слушателю этот запрос. Есть ли решение для этого?

0
20 июля '16 в 16:01
источник поделиться
1 ответ

В описываемом вами сценарии я могу рекомендовать использование экземпляров класса java.util.concurrent.CyclicBarrier - см. Следующий простой пример потока, выполняющего некоторые вычисления асинхронно (здесь сводится к установке значения "процентное значение" сначала до 50 а затем до 100) и позволяет получить значение "процентное значение" из основного потока.

Здесь класс SUT:

public class AsyncProcess implements Runnable {

  private int percentageDone = 0;

  public int getPercentageDone() { return percentageDone; }

  public void doFirstHalf() { percentageDone = 50; }

  public void doSecondHalf() { percentageDone = 100; }

  public void run() {
    doFirstHalf();
    doSecondHalf();
  }

}

Для модульного тестирования я использую JUnit 4 с шпионами Mockito для перехвата двух методов расчета моего SUT (в качестве альтернативы вы можете использовать свою любимую структуру AOP).

import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertThat;
import static org.mockito.Mockito.doAnswer;

import java.util.concurrent.CyclicBarrier;

import org.junit.Test;
import org.mockito.Mockito;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;

public class AsyncProcessTest {

  @Test
  public void testExecute() throws Exception {

    final CyclicBarrier firstHalfStarted = new CyclicBarrier(2);
    final CyclicBarrier firstHalfFinished = new CyclicBarrier(2);
    final CyclicBarrier secondHalfStarted = new CyclicBarrier(2);
    final CyclicBarrier secondHalfFinished = new CyclicBarrier(2);

    AsyncProcess process = Mockito.spy(new AsyncProcess());

    doAnswer(new Answer() {
      public Object answer(InvocationOnMock invocation) throws Throwable {
        firstHalfStarted.await();
        invocation.callRealMethod();
        firstHalfFinished.await();
        return null;
      }
    }).when(process).doFirstHalf();

    doAnswer(new Answer() {
      public Object answer(InvocationOnMock invocation) throws Throwable {
        secondHalfStarted.await();
        invocation.callRealMethod();
        secondHalfFinished.await();
        return null;
      }
    }).when(process).doSecondHalf();

    new Thread(process, "AsyncProcess").start();

    assertThat(process.getPercentageDone(), is(0));

    firstHalfStarted.await();
    firstHalfFinished.await();

    assertThat(process.getPercentageDone(), is(50));

    secondHalfStarted.await();
    secondHalfFinished.await();

    assertThat(process.getPercentageDone(), is(100));
  }

}

Такой подход позволяет осуществлять очень мелкозернистый контроль потока выполнения задействованных потоков. Его можно легко распространить на "микро-каркас" путем инкапсуляции пар CyclicBarriers и извлечения вызовов перехвата, а также поддержки тайм-аутов и правильной обработки исключений.

1
22 июля '16 в 12:44
источник

Посмотрите другие вопросы по меткам или Задайте вопрос