Mockito-Java8 — это набор дополнений Mockito, использующих Java 8 и лямбда-выражения, которые делают Mockito еще более компактным.
В начале 2015 года я выступил с докладом по флеш-памяти. Java 8 дает возможность тестировать! на GeeCON TDD 2015 и DevConf.cz 2015. В своей речи на 4 примерах я показал, как Java 8, а именно лямбда-выражения, может упростить инструменты тестирования и тестирования в целом. Одним из таких инструментов был Mokcito. Чтобы не позволить моему PoC-коду умереть на слайдах и сделать его просто доступным для других, я выпустил небольшой проект с двумя полезными в указанном случае дополнениями Java 8 для Mockito.
Быстрое введение
В качестве предпосылки предположим, что у нас есть следующая структура данных:
1
2
3
4
5
|
@Immutable class ShipSearchCriteria { int minimumRange; int numberOfPhasers; } |
Библиотека предоставляет два дополнения:
Lambda matcher — позволяет определить логику соответствия в лямбда-выражении.
1
2
|
given(ts.findNumberOfShipsInRangeByCriteria( argLambda(sc -> sc.getMinimumRange() > 1000 ))).willReturn( 4 ); |
Argument Captor — Java 8 edition — позволяет использовать ArgumentCaptor
в одну строку (здесь с AssertJ):
1
2
|
verify(ts).findNumberOfShipsInRangeByCriteria( assertArg(sc -> assertThat(sc.getMinimumRange()).isLessThan( 2000 ))); |
Лямбда-матчер
С помощью статического метода argLambda
создается экземпляр лямбда-сопоставителя, который можно использовать для определения логики сопоставления в лямбда-выражении (здесь для заглушки). Это может быть особенно полезно при работе со сложными классами, передаваемыми в качестве аргумента.
01
02
03
04
05
06
07
08
09
10
11
12
|
@Test public void shouldAllowToUseLambdaInStubbing() { //given given(ts.findNumberOfShipsInRangeByCriteria( argLambda(sc -> sc.getMinimumRange() > 1000 ))).willReturn( 4 ); //expect assertThat(ts.findNumberOfShipsInRangeByCriteria( new ShipSearchCriteria( 1500 , 2 ))).isEqualTo( 4 ); //expect assertThat(ts.findNumberOfShipsInRangeByCriteria( new ShipSearchCriteria( 700 , 2 ))).isEqualTo( 0 ); } |
Для сравнения та же логика, реализованная с пользовательским Ответом в Java 7:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
|
@Test public void stubbingWithCustomAsnwerShouldBeLonger() { //old way //given given(ts.findNumberOfShipsInRangeByCriteria(any())).willAnswer( new Answer<Integer>() { @Override public Integer answer(InvocationOnMock invocation) throws Throwable { Object[] args = invocation.getArguments(); ShipSearchCriteria criteria = (ShipSearchCriteria) args[ 0 ]; if (criteria.getMinimumRange() > 1000 ) { return 4 ; } else { return 0 ; } } }); //expect assertThat(ts.findNumberOfShipsInRangeByCriteria( new ShipSearchCriteria( 1500 , 2 ))).isEqualTo( 4 ); //expect assertThat(ts.findNumberOfShipsInRangeByCriteria( new ShipSearchCriteria( 700 , 2 ))).isEqualTo( 0 ); } |
Даже Java 8 и менее читаемые конструкции не слишком помогают:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
|
@Test public void stubbingWithCustomAsnwerShouldBeLongerEvenAsLambda() { //old way //given given(ts.findNumberOfShipsInRangeByCriteria(any())).willAnswer(invocation -> { ShipSearchCriteria criteria = (ShipSearchCriteria) invocation.getArguments()[ 0 ]; return criteria.getMinimumRange() > 1000 ? 4 : 0 ; }); //expect assertThat(ts.findNumberOfShipsInRangeByCriteria( new ShipSearchCriteria( 1500 , 2 ))).isEqualTo( 4 ); //expect assertThat(ts.findNumberOfShipsInRangeByCriteria( new ShipSearchCriteria( 700 , 2 ))).isEqualTo( 0 ); } |
Аргумент Captor — Java 8 издание
Статический метод assertArg
создает сопоставление аргументов, реализация которого внутренне использует ArgumentMatcher
с утверждением, предоставленным в лямбда-выражении. В приведенном ниже примере AssertJ предоставляет значимое сообщение об ошибке, но можно использовать любые утверждения (например, native от TestNG или JUnit) (если это действительно необходимо). Это позволяет встроить ArgumentCaptor:
1
2
3
4
5
6
7
8
|
@Test public void shouldAllowToUseAssertionInLambda() { //when ts.findNumberOfShipsInRangeByCriteria(searchCriteria); //then verify(ts).findNumberOfShipsInRangeByCriteria( assertArg(sc -> assertThat(sc.getMinimumRange()).isLessThan( 2000 ))); } |
По сравнению с 3 линиями классическим способом:
01
02
03
04
05
06
07
08
09
10
|
@Test public void shouldAllowToUseArgumentCaptorInClassicWay() { //old way //when ts.findNumberOfShipsInRangeByCriteria(searchCriteria); //then ArgumentCaptor<ShipSearchCriteria> captor = ArgumentCaptor.forClass(ShipSearchCriteria. class ); verify(ts).findNumberOfShipsInRangeByCriteria(captor.capture()); assertThat(captor.getValue().getMinimumRange()).isLessThan( 2000 ); } |
Резюме
Представленные дополнения были созданы как PoC для моей речи на конференции, но должны быть полностью функциональными и потенциально полезными в конкретных случаях. Чтобы использовать его в своем проекте, достаточно использовать Mockito 1.10.x или 2.0.x-beta, добавить mockito-java8
в качестве зависимости и, конечно же, скомпилировать ваш проект с Java 8+.
- Более подробная информация доступна на веб-странице проекта: https://github.com/szpak/mockito-java8
Ссылка: | Более компактный Mockito с Java 8, лямбда-выражениями и надстройками Mockito-Java8 от нашего партнера по JCG Марцина Заячковского в блоге Solid Soft . |