Сообщение довольно очевидно: вы не можете высмеивать невидимые и окончательные классы. Краткий ответ: Создайте именованный класс из своего анонимного и вместо этого протестируйте этот класс !
Длинный ответ, давайте копать почему!
Анонимный класс является окончательным
Вы создаете экземпляр анонимного класса FilterFactory
, когда компилятор видит анонимный класс, он создает final и видимый пакет класс. Таким образом, анонимный класс не может быть смоделирован с помощью стандартного средства, то есть с помощью Mockito.
Насмешливый анонимный класс: возможно, но БРИТЛ, если не ХАКИ
Хорошо, теперь предположим, что вы хотите смоделировать этот анонимный класс с помощью Powermock. Текущие компиляторы компилируют анонимный класс по следующей схеме:
Declaring class + $ + <order of declaration starting with 1>
Насмешливый анонимный класс возможен, но хрупок
Итак, если анонимный класс объявлен одиннадцатым, он будет выглядеть как
InputHelper$11.class
Таким образом, вы можете подготовиться к тестированию анонимного класса:
@RunWith(PowerMockRunner.class)
@PrepareForTest({InputHelper$11.class})
public class InputHelperTest {
@Test
public void anonymous_class_mocking works() throws Throwable {
PowerMockito.spy(InputHelper.BZIP2_FACTORY); // This line fails
}
}
Этот код скомпилируется, НО в конечном итоге будет сообщено как ошибка в вашей IDE. IDE, вероятно, не знает о InputHelper$11.class
. IntelliJ, который не использует скомпилированный класс для проверки отчета о коде так.
Также тот факт, что именование анонимного класса на самом деле зависит от порядка объявления, является проблемой, когда кто-то ранее добавляет другой анонимный класс, нумерация может измениться.
Анонимные классы созданы, чтобы оставаться анонимными, что если парни из компилятора решат однажды использовать буквы или даже случайные идентификаторы!
Так что издеваться над анонимными классами через Powermock возможно, но хрупко, никогда не делайте этого в реальном проекте!
РЕДАКТИРОВАННОЕ ПРИМЕЧАНИЕ: Компилятор Eclipse имеет другую схему нумерации, он всегда использует трехзначное число:
Declaring class + $ + <pad with 0> + <order of declaration starting with 1>
Также я не думаю, что JLS четко определяет, как компиляторы должны называть анонимные классы.
Вы не переназначаете шпиона на статическое поле
PowerMockito.spy(InputHelper.BZIP2_FACTORY); // This line fails
whenNew(BufferedInputStream.class).withArguments(in).thenReturn(buffer);
whenNew(CBZip2InputStream.class).withArguments(buffer).thenReturn(expected);
InputStream observed = InputHelper.BZIP2_FACTORY.makeFilter(in);
PowerMockito.spy
возвращает шпиона, но не меняет значение InputHelper.BZIP2_FACTORY
. Таким образом, вам нужно было бы установить с помощью отражения это поле. Вы можете использовать утилиту Whitebox
, которую предоставляет Powermock.
Заключение
Слишком много хлопот, чтобы просто протестировать с имитациями, что анонимный фильтр использует BufferedInputStream
.
Альтернативные
Я бы лучше написал следующий код:
Помощник ввода, который будет использовать именованный класс, я не использую имя интерфейса, чтобы прояснить пользователю, для чего предназначен этот фильтр!
public class InputHelper {
public static final BufferedBZIP2FilterFactory BZIP2_FACTORY = new BufferedBZIP2FilterFactory();
}
А теперь сам фильтр:
public class BufferedBZIP2FilterFactory {
public InputStream makeFilter(InputStream in) {
BufferedInputStream buffer = new BufferedInputStream(in);
return new CBZip2InputStream(buffer);
}
}
Теперь вы можете написать такой тест:
@RunWith(PowerMockRunner.class)
public class BufferedBZIP2FilterFactoryTest {
@Test
@PrepareForTest({BufferedBZIP2FilterFactory.class})
public void wraps_InputStream_in_BufferedInputStream() throws Exception {
whenNew(CBZip2InputStream.class).withArguments(isA(BufferedInputStream.class))
.thenReturn(Mockito.mock(CBZip2InputStream.class));
new BufferedBZIP2FilterFactory().makeFilter(anInputStream());
verifyNew(CBZip2InputStream.class).withArguments(isA(BufferedInputStream.class));
}
private ByteArrayInputStream anInputStream() {
return new ByteArrayInputStream(new byte[10]);
}
}
Но в конечном итоге можно было бы избежать штурма в этом тестовом сценарии, если вы заставите CBZip2InputStream
принимать только BufferedInputStream
. Обычно использование Powermock означает, что что-то не так с дизайном. На мой взгляд, Powermock отлично подходит для устаревших программ, но может слепить разработчиков при разработке нового кода; поскольку они упускают суть хорошей части ООП, я бы даже сказал, что они разрабатывают устаревший код.
Надеюсь, это поможет!