Почему Mockito не высмеивает статические методы?
Я прочитал несколько веток здесь о статических методах, и я думаю, что я понимаю проблемы, которые могут вызвать неправильное использование / чрезмерное использование статических методов Но я не до конца понял, почему трудно высмеивать статические методы.
Я знаю, что другие фреймворки, такие как PowerMock, могут это делать, но почему не может Mockito?
Я читал эту статью, но автор, кажется, религиозно против слова static
, может быть, это мое плохое понимание.
Простое объяснение / ссылка было бы здорово.
5 ответов
Я думаю, что причина может заключаться в том, что библиотеки фиктивных объектов обычно создают фиктивные путем динамического создания классов во время выполнения (используя cglib). Это означает, что они либо реализуют интерфейс во время выполнения (это то, что делает EasyMock, если я не ошибаюсь), либо они наследуют от класса для имитации (это то, что делает Mockito, если я не ошибаюсь). Оба подхода не работают для статических членов, так как вы не можете переопределить их, используя наследование.
Единственный способ макетировать статику - это модифицировать байт-код класса во время выполнения, что, я полагаю, немного сложнее, чем наследование.
Это мое предположение, чего стоит...
Если вам нужно издеваться над статическим методом, это сильный показатель плохого дизайна. Обычно вы издеваетесь над зависимостью вашего тестируемого класса. Если ваш тестируемый класс ссылается на статический метод - например, java.util.Math#sin - это означает, что тестируемому классу нужна именно эта реализация (например, точности и скорости). Если вы хотите абстрагироваться от конкретной реализации пазухи, вам, вероятно, нужен интерфейс (вы видите, к чему это приведет)?
Mockito [3.4.0] может имитировать статические методы!
Заменить
mockito-core
зависимость отmockito-inline:3.4.0
.Класс со статическим методом:
class Buddy { static String name() { return "John"; } }
Использовать новый метод
Mockito.mockStatic()
:@Test void lookMomICanMockStaticMethods() { assertThat(Buddy.name()).isEqualTo("John"); try (MockedStatic<Buddy> theMock = Mockito.mockStatic(Buddy.class)) { theMock.when(Buddy::name).thenReturn("Rafael"); assertThat(Buddy.name()).isEqualTo("Rafael"); } assertThat(Buddy.name()).isEqualTo("John"); }
Mockito заменяет статический метод в
try
только блок.
Я серьезно думаю, что это запах кода, если вам тоже нужно высмеивать статические методы.
- Статические методы для доступа к общей функциональности? -> Используйте экземпляр синглтона и введите
- Сторонний код? -> Оберните его в свой собственный интерфейс / делегат (и при необходимости сделайте его тоже одиночным)
Единственный раз, когда это кажется мне излишним, это библиотеки вроде Guava, но вам не нужно все равно издеваться над этим, потому что это часть логики... (например, Iterables.transform(..))
Таким образом, ваш собственный код остается чистым, вы можете чистым образом смоделировать все ваши зависимости, и у вас есть антикоррупционный уровень против внешних зависимостей. Я видел PowerMock на практике, и все классы, для которых он был нужен, были плохо спроектированы. Также временами интеграция PowerMock вызывала серьезные проблемы
(например, https://code.google.com/p/powermock/issues/detail?id=355)
PS: То же самое относится и к частным методам. Я не думаю, что тесты должны знать о деталях частных методов. Если класс настолько сложен, что возникает соблазн издеваться над частными методами, это, вероятно, признак разделения этого класса...
В дополнение к ответу Герольда Брозера вот пример имитации статического метода с аргументами:
class Buddy {
static String addHello(String name) {
return "Hello " + name;
}
}
...
@Test
void testMockStaticMethods() {
assertThat(Buddy.addHello("John")).isEqualTo("Hello John");
try (MockedStatic<Buddy> theMock = Mockito.mockStatic(Buddy.class)) {
theMock.when(() -> Buddy.addHello("John")).thenReturn("Guten Tag John");
assertThat(Buddy.addHello("John")).isEqualTo("Guten Tag John");
}
assertThat(Buddy.addHello("John")).isEqualTo("Hello John");
}
Mockito возвращает объекты, но static означает "уровень класса, а не уровень объекта", поэтому mockito выдаст исключение нулевого указателя для static.
В некоторых случаях статические методы могут быть сложны для тестирования, особенно если их необходимо смоделировать, поэтому большинство фреймворк-фреймворков их не поддерживают. Я обнаружил, что это сообщение в блоге очень полезно для определения того, как высмеивать статические методы и классы.