#java #unit-testing #junit #mockito
#java #модульное тестирование #junit #mockito
Вопрос:
У меня есть метод, который я должен протестировать. Код (конечно, некоторые части были вырезаны):
public class FilterDataController {
public static final String DATE_FORMAT = "yyyy-MM-dd";
@Autowired
private FilterDataProvider filterDataProvider;
@ApiOperation(value = "Get possible filter data",response = ResponseEntity.class)
@ApiResponses(value = {
@ApiResponse(...),
@ApiResponse(...)})
@RequestMapping(path = "...", method = RequestMethod.GET)
public ResponseEntity<Object> getPossibleFilterData(
@RequestParam(value = "startDate") @DateTimeFormat(pattern=DATE_FORMAT) final Date startDate,
@RequestParam(value = "endDate") @DateTimeFormat(pattern=DATE_FORMAT) final Date endDate) {
if (endDate.compareTo(startDate) == -1){
throw new ValueNotAllowedException("End date should be after or equal start date");
}
else {
Date newEndDate = endDate;
if (startDate.equals(endDate)){
newEndDate = new Date(endDate.getTime() TimeUnit.DAYS.toMillis(1) - 1);
}
List<String> possibleCountries = Lists.newArrayList(filterDataProvider.getPossibleCountries(startDate, newEndDate));
return new ResponseEntity<>(new FilterResponse(possibleCountries),HttpStatus.OK);
}
}
}
Вопрос: как проверить if-оператор в методе getPossibleFilterData
с помощью Mockito и JUnit? Я хочу передать методу равные даты, а затем проверить, правильно ли работает мой if-оператор.
Комментарии:
1. Как упоминалось во многих ответах ниже, ваши тесты не должны предполагать вашу реализацию: что, если вы реорганизуете метод, чтобы он возвращал правильные значения, но вообще не использовал
if
оператор? Вместо того, чтобы предполагать это, передайте ошибочные значения и пройдите тест только тогда, когда поймаете правильное исключение .2. @JeffBowman спасибо за совет.
Ответ №1:
Если вы действительно хотите чистый модульный тест, а не интеграционный тест, вы можете полагаться на аннотацию @Mock
, чтобы смоделировать свой сервис FilterDataProvider
и @InjectMocks
внедрить свой макет в свой экземпляр FilterDataController
.
Тогда вы могли бы предложить 3 теста:
- Один тест, в котором даты указаны правильно, но разные,
- Еще один, где даты исправлены, но равны
- И последний, где даты неверны, который будет выброшен
ValueNotAllowedException
, который можно было бы протестировать из коробки, используя@Test(expected = ValueNotAllowedException.class)
.
Если вам нужно убедиться, что filterDataProvider.getPossibleCountries(startDate, newEndDate)
он был вызван с ожидаемыми аргументами, которые вам нужно использовать verify
.
Тогда код будет примерно таким:
@RunWith(MockitoJUnitRunner.class)
public class FilterDataControllerTest {
@Mock
FilterDataProvider filterDataProvider;
@InjectMocks
FilterDataController controller;
@Test(expected = ValueNotAllowedException.class)
public void testGetPossibleFilterDataIncorrectDates() {
controller.getPossibleFilterData(new Date(1L), new Date(0L));
}
@Test
public void testGetPossibleFilterDataCorrectDates() {
// Make the mock returns a list of fake possibilities
Mockito.when(
filterDataProvider.getPossibleCountries(
Mockito.anyObject(), Mockito.anyObject()
)
).thenReturn(Arrays.asList("foo", "bar"));
ResponseEntity<Object> response = controller.getPossibleFilterData(
new Date(0L), new Date(1L)
);
Assert.assertEquals(HttpStatus.OK, response.getStatusCode());
// Make sure that
// filterDataProvider.getPossibleCountries(new Date(0L), new Date(1L))
// has been called as expected
Mockito.verify(filterDataProvider).getPossibleCountries(
new Date(0L), new Date(1L)
);
// Test response.getBody() here
}
@Test
public void testGetPossibleFilterDataEqualDates() {
// Make the mock returns a list of fake possibilities
Mockito.when(
filterDataProvider.getPossibleCountries(
Mockito.anyObject(), Mockito.anyObject()
)
).thenReturn(Arrays.asList("foo", "bar"));
// Call the controller with the same dates
ResponseEntity<Object> response = controller.getPossibleFilterData(
new Date(1L), new Date(1L)
);
Assert.assertEquals(HttpStatus.OK, response.getStatusCode());
Mockito.verify(filterDataProvider).getPossibleCountries(
new Date(1L), new Date(TimeUnit.DAYS.toMillis(1))
);
// Test response.getBody() here
}
}
Комментарии:
1. спасибо за код. Но как проверить, что
filterDataProvider
это было вызвано с правильными датами? Я хочу отправить наgetPossibleFilterData
равные даты, а затем проверить, чтоfilterDataProvider.getPossibleCountries(startDate, newEndDate)
вызывается с исправленными датами.
Ответ №2:
Вам придется издеваться FilterDataProvider
, а затем вводить это в свой тестовый класс с помощью InjectMocks.
getPossibleFilterData
это будет тестируемый метод, поэтому выберите любую конкретную дату (используйте Calendar.set(...)
, затем Calendar.getTime()
) и отправьте эту же дату в качестве начальной и конечной даты.
Теперь после getPossibleFilterData
завершения вы можете проверить, был ли filterDataProvider.getPossibleCountries
вызван с конечной датой, которая на одну миллисекунду больше даты начала. Это можно сделать Calendar.getTimeInMillis()
внутри метода mocked класса или путем проверки с помощью Mockito с датой, которая на одну миллисекунду больше, чем дата, которая была указана изначально.
Редактировать: приведен пример кода:
public class FilterDataControllerTest {
@Test
public void testSameDate() {
FilterDataProvider provider = Mockito.mock(FilterDataProvider.class);
FilterDataController controller = new FilterDataController(provider);
Date startDate = new GregorianCalendar(2016, Calendar.JANUARY, 11).getTime();
Date endDate = new GregorianCalendar(2016, Calendar.JANUARY, 11).getTime();
Date expectedEndDate = new Date(endDate.getTime() TimeUnit.DAYS.toMillis(1) - 1);
controller.getPossibleFilterData(startDate, endDate);
Mockito.verify(provider).getPossibleCountries(Mockito.eq(startDate), Mockito.eq(expectedEndDate));
}
}
Комментарии:
1. «или путем проверки с помощью Mockito» — можете ли вы объяснить, как это сделать? Мой первоначальный вопрос был об этом (как проверить, что EndDate был изменен в случае EndDate==StartDate . Такой случай соответствует внутреннему if-оператору).
2. Для вашего кода требуется написать конструктор
FilterDataController
, но в моей ситуации это неуместно. Но спасибо за такой подход. Я запомню это. 1
Ответ №3:
Я вижу два основных подхода.
- Использование функциональности Mockito: если вы вводите в свой контроллер макет FilterDataProvider (что является стандартным подходом, использующим, например, MockitoJUnitRunner и @InjectMocks), вы можете использовать опцию Mockito «verity», чтобы убедиться, что он получил правильную конечную дату. Смотрите обсуждение: http://www.vogella.com/tutorials/Mockito/article.html#mockito_verify
- Очевидно, что существуют другие подходы, которые основаны на логике, а не на технических деталях. Например: рефакторинг части «if» в отдельный метод «correctEndDate» или заполнение ваших данных так, чтобы на основе конечной даты возвращался другой список стран.
Комментарии:
1. спасибо за
verify
. Есть примерverify(test).testing(Matchers.eq(12));
. Но в моем случае я хочу убедиться, что метод в моем методе был вызван с исправленными датами. Может быть, что-то вроде этогоnewEndDate = ...; verify(FilterDataController).getPossibleFilterData().getPossibleCountries(Matchers.eq(startDate, newEndDate));