Ошибка модульного теста при тестировании кода с использованием ScheduledExecutorService # scheduleAtFixedRate - PullRequest
0 голосов
/ 24 апреля 2020

На примере воспроизводимого примера у меня есть следующий класс

public class SampleCaching {

    ScheduledExecutorService executorService;
    @com.google.inject.Inject InterestCache interestCache;
    @Inject MultimediaCache multimediaCache;

    @Inject
    public SampleCaching(InterestCache interestCache, MultimediaCache multimediaCache) {
        this.executorService = Executors.newScheduledThreadPool(3);
        this.interestCache = interestCache;
        this.multimediaCache = multimediaCache;
    }

    protected void calculate() {
        interestCache.populateOne();
        interestCache.populateTwo();
        multimediaCache.populateMultimedia();
        log.info("Cache population completed!");
    }

    public void start() {
        executorService.scheduleAtFixedRate(this::calculate, 
                                0, 20, TimeUnit.MINUTES); // notice initial delay 
    }
}

, и, как мне кажется, факт состоит в том, что я написал полу-неправильный модульный тест для этого кода, который читается как:

@org.junit.runner.RunWith(PowerMockRunner.class)
@org.powermock.core.classloader.annotations.PowerMockIgnore("javax.management.*")
public class SampleCachingTest {

    @org.mockito.Mock InterestCache interestCache;
    @Mock MultimediaCache multimediaCache;
    @org.mockito.InjectMocks SampleCaching sampleCaching;

    @Test
    public void testInvokingStart() throws Exception {
        sampleCaching.start();
        verify(multimediaCache, times(0)).populateMultimedia();
        verify(interestCache, times(0)).populateOne();
        verify(interestCache, times(0)).populateTwo();
    }
}
  • Я прошел Mocking ScheduledExecutorService.scheduleWithFixedDelay (...) возвращает ноль , но я не знаю, по каким причинам мне следует требовать насмешливости для возвращаемого типа scheduleAtFixedRate здесь и в любом случае макеты работают нормально со мной?
  • Прошли через Как провести модульное тестирование фрагмента кода, работающего внутри службы executor, вместо этого ожидая на Thread.sleep (time) , чтобы найти наиболее ответ с повышенным голосом, мало связанный с тестом ScheduledExecutorService (по крайней мере, я так думаю сейчас) начальная задержка в действительном коде, скажем, для примера 1 MINUTE.

    Что меня действительно заставило спросить, так это то, что если я изменю тест на

    @Test
    public void testInvokingStart() throws Exception {
        sampleCaching.start();
        verify(interestCache, times(1)).populateOne();
        verify(interestCache, times(1)).populateTwo();
    }
    

    , то это всегда выполняет су К счастью, добавление verify для мультимедиа не всегда проходит тест с другой стороны:

    verify(multimediaCache, times(1)).populateMultimedia(); // or even to `times(0)`
    

    Существует ли причина такого поведения (детерминированность c или детерминированность c)? Как правильно исправить этот тест?

1 Ответ

1 голос
/ 25 апреля 2020

Таким образом, вы запускаете метод SampleCaching # start самостоятельно, это, в свою очередь, говорит ScheduledExecutorService, чтобы вызвать метод вычисления с начальной задержкой 0 секунд. Это будет происходить в отдельном потоке. Тем временем ваш тестовый код продолжает выполняться, и в следующий раз он проверяет, что метод populateMultimedia не был вызван в вашем multimediaCache. и то же самое для populateOne и populateTwo. Успех этого будет зависеть от прогресса, достигнутого методом вычисления в другом потоке, который был запущен. Если он уже вызвал метод populateMultimedia, то ваша первая проверка не удастся, как и остальные. Если, с другой стороны, он не продвинулся так далеко, тест будет успешным, но, возможно, он не будет выполнен в populateOne или populateTwo.

Вам либо нужно встроить механизм синхронизации (например, java .util. concurrent.CountDownLatch) этот ваш метод вычисления выполняет countDown в конце, а ваш тестовый код ожидает перед проверкой или вы установили разумную задержку между вызовом метода start и вызовами проверки. Первый является навязчивым, поскольку он изменяет компонент, который вы тестируете. Вы можете подумать о создании подкласса SimpleCaching, который переопределяет метод вычисления, но, опять же, это навязчиво, если ваш метод вычисления является закрытым.

...