Как я могу легко измотать статический метод в 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 для перехвата вызова статического метода и сделать что-то полезное для вашего теста.