Мокито — мой любимый маленький помощник, если он собирается написать легкие тесты JUnit . При необходимости очень легко заменить «реальные» зависимости тестируемого модуля на макеты. В частности, при работе с интерфейсом API от границы до фреймворка такие зависимости в противном случае могут быть очень дорогими в настройке.
Но иногда ситуация немного сложнее. Например, если тест должен по какой-то причине взаимодействовать хотя бы с одним реальным экземпляром, который принадлежит такой платформе. Если это взаимодействие включает в себя передачу макета в качестве параметра этому экземпляру, то при удачной реализации реализация преобразует параметр в тип, который неизвестен с точки зрения интерактора.
Вот простой пример, чтобы прояснить это:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
public interface Foo { [...] } public class Bar { public Bar( Foo foo ) { Runnable runnable = ( Runnable )foo; runnable.run(); } [...] } public class SomeTest { @Test public void testConstructor() { Foo fooMock = mock( Foo. class ); // fails with ClassCastException Bar bar = new Bar( fooMock ); [...] } } |
Думайте о классе Bar
как о фреймворковом коде, который ожидает определенный вид реализации. Поскольку тип параметра Foo
не отражает это ожидание, передача макета Foo
в конструктор Bar
приведет к сбою теста с ClassCastException
.
Возможно, первая мысль, которая приходит вам в голову, когда вы смотрите на ситуацию выше, состоит в том, что фреймворк отстой, приводя к необъявленному типу, и что лучше, выбрасывать все и начинать все заново!
К сожалению, существуют ситуации реального мира, где такое поведение, возможно, допустимо. Например, платформа Eclipse имеет множество интерфейсов, которые объявлены как «не предназначенные для реализации клиентами». Хорошим примером для этого является интерфейс IHistoryView
командного API. Работая с платформой 3.x, можно быть уверенным, что реализация IViewPart
расширяет IViewPart
, хотя это деталь, которая не раскрывается интерфейсом представления истории.
Учитывая эти обстоятельства, иногда может быть желательно создать макет нескольких типов — макет, который реализует IViewPart
и IViewPart
— хотя API не указывает все из них. Mockito облегчает это с помощью менее известной возможности конфигурации MockSettings#extraInterfaces
. В следующем фрагменте показано, как использовать extraInterfaces
для исправления теста в приведенном выше примере.
01
02
03
04
05
06
07
08
09
10
|
@Test public void testConstructor() { Foo mock = mock( Foo. class , withSettings().extraInterfaces( Runnable. class ) ); // the mock now supports the cast to runnable Bar bar = new Bar( mock ); [...] } |
Вызов метода withSettings
создает новый экземпляр MockSettings
который настраивается с дополнительным типом Runnable
. Сгенерированный макет Foo
реализует Foo
и Runnable
. И сейчас тест проходит.
Однако имейте в виду, что хотя мотивация для использования дополнительных интерфейсов в этом посте может показаться правдоподобной, нельзя не подчеркнуть, что вам стоит подумать дважды, прежде чем использовать эту функцию. Или, как гласит документация: «Если вам случается использовать его часто, убедитесь, что вы действительно производите простой, чистый и читаемый код». И небрежно использованный это определенно предопределенный переломный момент.
Ссылка: | Что такое дополнительные интерфейсы Mockito? от нашего партнера JCG Фрэнка Аппеля в блоге Code Affine . |