Как написать тестовый пример Junit для метода, который имеет логику повторных попыток

Метод повторных попыток точно просматривает папку для определенного файла и возвращает файл, если он присутствует. Он имеет максимальное число повторов для 3 и спит в течение 1 минуты между 2 повторными попытками. Если после отсутствия файла максимальных повторений он вызывает некоторое исключение

метод что-то вроде этого

File getFile(int retryCount){
    File file;
    file =getFilefromLocation();
    if(file!=null) return file
    if(file==null and retryCount>0)
    Thread.sleep(1)
     --retryCount;
    File filePicked =getFile( retryCount)
   }
else return null;
}

3 ответа

Один из возможных способов - выделить часть, которая проверяет наличие файла, в свой собственный объект и смоделировать этот объект.

Я постараюсь дать несколько указателей.

1) Здесь я не буду издеваться над всей логикой, чтобы проверить наличие файла, так как он является частью поведения тестируемого метода.
То, что вы хотите высмеять, это та часть, где вы на самом деле пытаетесь получить файл, то есть когда вы вызываете getFilefromLocation(),

2) Использование короткой задержки между повторными попытками - идея, но достаточно ли этого, чтобы сделать ваш тест надежным?
Не совсем, потому что вы также должны утверждать, что Thread.sleep() был вызван и с ожидаемым значением. Не проверять, что в модульном тесте означает, что любой может удалить Thread.sleep() в getFile() метод и тест все равно пройдут. Вы действительно не хотите этого.
Обратите внимание, что вы не можете легко высмеять это: это static, Вы должны переместить это утверждение в DelayService что ты смеешься

3) На самом деле вы параметризуете retryCount, Когда вы пишете модульные тесты, вы хотите проверить поведение компонентов в соответствии с вашими требованиями. Таким образом, вы также должны убедиться, что класс, который зависит от getFile(int retryCount) эффективно передает ожидаемый параметр: 3,
Кроме того, если вы не хотите разрешать более определенного числа повторов, вы также должны добавить эту проверку в метод и утвердить это правило в модульном тесте.

Согласно двум первым пунктам, ваш реальный код может быть изменен на:

private FileLocator fileLocator; // added dependency
private DelayService delayService; // added dependency

//  constructor to set these dependencies
public MyService(FileLocator fileLocator, DelayService delayService){
    ...
}

File getFile(int retryCount){
    File file;
    file = fileLocator.getFilefromLocation(); // -> change here
    if(file!=null) return file
    if(file==null and retryCount>0){
       delayService.waitFor(); // -> other change here
       --retryCount;
       File filePicked = getFile(retryCount)
    }
    else return null;
}

Что касается модульного тестирования, вы можете использовать Mockito для проверки и смешивать "простые" модульные тесты с параметризованными тестами, так как некоторые сценарии имеют схожее поведение с отклонением количества фактических повторных попыток.
Например, повторная попытка 0, 1, 2 и 3 раза и поиск файла - это 4 случая, которые вы можете параметризировать.
И наоборот, если не удается найти файл, не требуется параметризация, так как все попытки будут сделаны для проверки поведения.

Вот пример кода, основанный на JUnit5 и Mockito (не протестирован), который вы можете адаптировать в соответствии с вашим реальным кодом. Я только что проиллюстрировал с помощью параметризованного теста. Другие тестовые примеры не должны быть более сложными для реализации.

import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
import org.junit.jupiter.api.Assertions;
import org.mockito.Mockito;
import org.mockito.Mock;

private static final long EXPECTED_DELAY_MN = 1; 
private static final long RETRY_COUNT = 3; 

@Mock
FileLocator fileLocatorMock;

@Mock
private DelayService delayServiceMock;


MyServiceTest myServiceTest;

public MyServiceTest(){
   myServiceTest = new MyServiceTest(fileLocatorMock, delayServiceMock);
}

@ParameterizedTest
@ValueSource(ints = { 0, 1, 2, 3 })
public void getFileThatFailsMultipleTimeAndSuccess(int nbRetryRequired){    

    // failing find
    for (int i=0; i < nbRetryRequired; i++) {
        Mockito.when(fileLocatorMock.getFilefromLocation(...)).thenReturn(null);
    }

    // successful find
    File fileByMock = new File(...); //fake file
    Mockito.when(fileLocatorMock.getFilefromLocation(...)).thenReturn(fileByock);
    File actualFile = myServiceTest.getFile(RETRY_COUNT);

    // assertions
    Mockito.verify(delayServiceMock, Mockito.times(nbRetryRequired)) 
           .waitFor();
    Assert.assertSame(fileByMock, actualFile);

   }
}

Я бы настроил время повтора и сделал бы его 1 секунду для тестирования. У вас могут быть тесты, где файл уже есть, а не там, и тот, где он добавлен как фоновый поток. Должно занять около 5 секунд.

Я также хотел бы рассмотреть более короткие повторные попытки чаще, например, 120 попыток каждую 1 секунду вместо 3 раз каждую минуту.

Другие вопросы по тегам