A +
соответствует одному или нескольким экземплярам предыдущего шаблона. *
соответствует нулю или более экземплярам предыдущего шаблона.
Итак, в основном, если вы используете +
, должен быть хотя бы один экземпляр шаблона, если вы используете *
, он будет по-прежнему совпадают, если нет экземпляров.
Сообщение довольно очевидно: вы не можете высмеивать невидимые и окончательные классы. Короткий ответ: создайте именованный класс вашего анонимного, и вместо этого протестируйте этот класс!
Длинный ответ, давайте разозлим почему!
Вы создаете анонимный класс FilterFactory
, когда компилятор видит анонимный класс, он создает конечный и видимый класс пакета. Таким образом, анонимный класс не является макетным по стандартным средним значениям, то есть через Mockito.
OK, теперь предположите, что вы хотите быть в состоянии издеваться над этим анонимный класс через Powermock. Текущие компиляторы компилируют анонимный класс со следующей схемой:
Declaring class + $ + <order of declaration starting with 1>
Mocking анонимный класс возможен, но хрупкий (и я имею в виду это). Предположим, что анонимный класс является одиннадцатым, который будет объявлен, он появится как
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 возможны, но хрупкие, никогда не делайте этого в real project!
EDITED ПРИМЕЧАНИЕ: Компилятор Eclipse имеет другую схему нумерации, он всегда использует 3-значное число:
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.
Слишком много проблем, чтобы просто проверить с помощью mocks, что анонимный фильтр использует 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]);
}
}
Но в конечном итоге это может привести к тому, что для этого тестового сценария не удастся использовать материал powermock, если вы вынудите CBZip2InputStream
принять только BufferedInputStream
. Обычно использование Powermock означает, что с дизайном что-то не так. По моему мнению, Powermock отлично подходит для устаревших программных продуктов, но может скрывать разработчиков при разработке нового кода; поскольку они упускают смысл хорошей части ООП, я бы даже сказал, что они разрабатывают устаревший код.
Надеюсь, что это поможет!
Старый пост, но вам не нужно создавать именованный класс - вместо этого используйте подстановочные знаки, как упоминалось в этом сообщении . Мощный конструктор powermock через whennew () не работает с анонимным классом
@PrepareForTest(fullyQualifiedNames = "com.yourpackage.containing.anonclass.*")
@PrepareForTest(fullyQualifiedNames = "com.yourpackage.YourClass*")
– gladed
23 September 2016 в 20:25
Я только что столкнулся с той же проблемой. Итак, согласно документации конструктора, издевающегося над , вам нужно подготовить класс, который создаст класс зла (ы). В вашем случае злые классы BufferedInputStream и CBZip2InputStream, а их создатель - анонимный класс, который не может быть определен в аннотации PrepareForTest. Поэтому я должен был сделать то же самое, что и вы (хм, просто увидел ваш комментарий), я переместил анонимный класс в названный класс.
Вам нужно запустить тест с помощью Runner PowerMockito, и вам нужно указать структуру, у которых класс (ы) должен иметь пользовательское поведение. Добавьте следующие классные аннотации к вашему тестовому классу:
@RunWith(PowerMockRunner.class)
@PrepareForTest({ BufferedInputStream.class })