Как я могу легко измотать статический метод в Java (jUnit4)
Как легко выставить статический метод в Java?
Я использую Spring 2.5 и JUnit 4.4
@Service
public class SomeServiceImpl implements SomeService {
public Object doSomething() {
Logger.getLogger(this.class); //a static method invoked.
// ...
}
}
Я не контролирую статический метод, который должен вызвать мой сервис, поэтому я не могу его реорганизовать, чтобы его можно было тестировать с большей степенью детализации. В качестве примера я использовал Log4J Logger, но реальный статический метод схож. Это не вариант изменения статического метода.
Выполнение работы Grails, я привык использовать что-то вроде:
def mockedControl = mockFor(Logger)
mockControl.demand.static.getLogger{Class clazz-> … }
…
mockControl.verify()
Как мне сделать что-то подобное в Java?
Ответы
Ответ 1
В принципе, нет простого способа сделать это в Java + Spring 2.5 и JUnit 4.4 на данный момент.
Хотя можно реорганизовать и отвлечь статический вызов, рефакторинг кода не является тем решением, которое я искал.
JMockit выглядел так, как будто это сработает, но несовместимо с Spring 2.5 и JUnit 4.4.
Ответ 2
Вы имеете в виду, что вы не можете контролировать код вызова? Потому что, если вы управляете вызовами статического метода, но не самой реализации, вы можете легко сделать это проверяемым. Создайте интерфейс зависимостей с помощью одного метода с той же сигнатурой, что и статический метод. Ваша производственная реализация просто вызовет статический метод, но все, что в настоящее время вызывает статический метод, вызовет через интерфейс.
Затем вы можете обыгрывать этот интерфейс обычным способом.
Ответ 3
Рамка JMockit promises, позволяющая издеваться над статическими методами.
https://jmockit.dev.java.net/
Фактически, он делает некоторые довольно смелые утверждения, в том числе, что статические методы - это абсолютно правильный выбор дизайна, и их использование не должно ограничиваться из-за неадекватности тестовых фреймворков.
Независимо от того, оправданы ли такие претензии, сама инфраструктура JMockit довольно интересна, хотя мне еще предстоит ее попробовать.
Ответ 4
PowerMock обладает этой способностью. Он также может макетировать экземпляры объектов внутри тестируемого класса. Если ваш протестированный метод вызывает новый Foo(), вы можете создать макет объекта для этого Foo и заменить его в методе, который вы тестируете.
Возможны также функции подавления конструкторов и статические инициализаторы. Все эти вещи считаются непроверяемым кодом и поэтому не рекомендуется делать, но если у вас есть устаревший код, его изменение не всегда является опцией. Если вы находитесь в этом положении, PowerMock может вам помочь.
Ответ 5
public interface LoggerWrapper {
public Logger getLogger(Class<?> c);
}
public class RealLoggerWrapper implements LoggerWrapper {
public Logger getLogger(Class<?> c) {return Logger.getLogger(c);}
}
public class MockLoggerWrapper implements LoggerWrapper {
public Logger getLogger(Class<?> c) {return somethingElse;}
}
Ответ 6
Как уже было сказано выше, JMockit может издеваться над статическими методами (и что-то еще).
Он даже имеет прямую поддержку для фреймворков регистрации. Например, вы можете написать:
@UsingMocksAndStubs(Log4jMocks.class)
public class SomeServiceTest
{
// All test methods in this class will have any calls
// to the Log4J API automatically stubbed out.
}
Однако поддержка JUnit 4.4 была отброшена. Поддерживаются JUnit 3.8, JUnit 4.5+ и TestNG 5.8+.
Ответ 7
Это одна из причин, почему статические методы являются плохими.
Мы перепроектировали большинство наших заводов, чтобы иметь сеттеры, чтобы мы могли установить в них макетные объекты. Фактически, мы придумали что-то близкое к инъекции зависимостей, когда один метод действовал как factory для всех наших синглетонов.
В вашем случае может быть добавлен метод Logger.setLogger() (и сохранение этого значения). Если вам нужно, вы можете расширить класс регистратора и затенять метод getLogger своим собственным.
Ответ 8
Вы можете использовать AspectJ для перехвата вызова статического метода и сделать что-то полезное для вашего теста.