Как я могу легко макетировать статический метод в Java (jUnit4) - PullRequest
15 голосов
/ 30 июля 2009

Как мне легко смоделировать статический метод в 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?

Ответы [ 8 ]

14 голосов
/ 30 июля 2009

Вы имеете в виду, что не можете контролировать код вызова? Потому что, если вы управляете вызовами от до статического метода, но не самой реализацией, вы можете легко сделать это тестируемым. Создайте интерфейс зависимостей с одним методом с той же сигнатурой, что и у статического метода. Ваша производственная реализация просто вызовет статический метод, но все, что в данный момент вызывает статический метод, будет вызывать через интерфейс.

Затем вы можете макетировать этот интерфейс обычным способом.

4 голосов
/ 02 августа 2009

PowerMock обладает этой способностью. Он также может высмеивать экземпляры объектов внутри тестируемого класса. Если ваш протестированный метод вызывает new Foo (), вы можете создать фиктивный объект для этого Foo и заменить его в тестируемом методе.

Возможны такие вещи, как подавление конструкторов и статических инициализаторов. Все эти вещи считаются непроверенным кодом и поэтому не рекомендуется делать, но если у вас есть устаревший код, его изменение не всегда является вариантом. Если вы находитесь в этом положении, PowerMock может вам помочь.

4 голосов
/ 30 июля 2009

Фреймворк JMockit обещает разрешить насмешку над статическими методами.

https://jmockit.dev.java.net/

На самом деле, он делает несколько довольно смелых заявлений, в том числе, что статические методы - совершенно правильный выбор дизайна, и их использование не должно быть ограничено из-за неадекватности тестирующих структур.

Независимо от того, являются ли такие заявления оправданными, сама структура JMockit довольно интересна, хотя я еще сам не пробовал ее.

1 голос
/ 02 августа 2009

Как уже говорилось в другом ответе, 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+.

1 голос
/ 30 июля 2009
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;}
    }
0 голосов
/ 31 августа 2009

В принципе, в Java + Spring 2.5 и JUnit 4.4 на данный момент нет простого способа сделать это.

Хотя можно выполнить рефакторинг и абстрагирование статического вызова, рефакторинг кода не является решением, которое я искал.

Выглядело, что JMockit работает, но несовместимо с Spring 2.5 и JUnit 4.4.

0 голосов
/ 30 июля 2009

Вы можете использовать AspectJ для перехвата статического вызова метода и сделать что-то полезное для вашего теста.

0 голосов
/ 30 июля 2009

Это одна из причин, почему статические методы плохие.

Мы перестроили большинство наших фабрик, чтобы у них также были сеттеры, чтобы мы могли устанавливать в них фиктивные объекты. Фактически, мы придумали что-то близкое к внедрению зависимостей, когда один метод работал как фабрика для всех наших синглетонов.

В вашем случае добавление метода Logger.setLogger () (и сохранение этого значения) может работать. Если вам нужно, вы можете расширить класс logger и создать тень на метод getLogger своим собственным.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...