#java #singleton #powermock #suppress
#java #одноэлементный #powermock #подавлять
Вопрос:
Я пытаюсь выполнить модульное тестирование некоторых классов, которые используют одноэлементный класс, конструктор которого выполняет некоторые вещи, которые я не могу (и не должен) выполнять из среды модульного тестирования. Моим идеальным сценарием было бы в конечном итоге полностью подавить конструктор, а затем отключить другие методы-члены, которые вызывают мои тестовые классы. Моя проблема в том, что, похоже, я не могу подавить конструктор.
Мое понимание способа решения этой проблемы было бы примерно следующим:
public class MySingleton extends AbstractSingletonParent {
public final static MySingleton Only = new MySingleton();
private MySingleton(){
super(someVar); // I want the super-class constructor to not be called
//
//more code I want to avoid
}
public Object stubbedMethod() {}
}
public class ClassToBeTested {
public void SomeMethod(){
Object o = MySingleton.Only.stubbedMethod();
}
}
@RunWith(PowerMockRunner.class)
@PrepareForTest(MySingleton.class)
public class TestClass {
@Test
public void SomeTest() {
suppress(constructor(MySingleton.class));
mockStatic(MySingleton.class);
PowerMock.replay(MySingleton.class);
// invoke ClassToBeTested, etc
PowerMock.verify(MySingleton.class);
//make some assertions
}
}
К сожалению, во время вызова createMock происходит сбой конструктора MySingleton, и он по-прежнему вызывает суперконструктор.
Я делаю что-то глупое? Я нашел в Интернете пример, делающий почти точно это, но в нем использовался устаревший метод suppressConstructor. Несмотря на устаревание, я тоже пробовал это, но безрезультатно…
Возможно ли то, что я пытаюсь сделать? Если да, то что я делаю не так?
* Отредактированная версия теперь работает.
Ответ №1:
Вам нужно пометить его TestClass
с помощью @PrepareForTest
аннотации, чтобы у него была возможность манипулировать байт-кодом одиночных элементов.
Кроме того, сигнатура подавления ctor суперкласса должна включать класс somevar
; прямо сейчас вы просто подавляете ctor по умолчанию.
Смотрите @PrepareForTest
документы API.Вот сообщение в блоге с некоторыми дополнительными подробностями.
Черт возьми, у меня это работает:
@RunWith(PowerMockRunner.class)
@PrepareForTest({EvilBase.class, NicerSingleton.class})
public class TestEvil {
@Test
public void testEvil() {
suppress(constructor(EvilBase.class));
assertEquals(69, EvilBase.getInstance().theMethod());
}
@Test
public void testNice() {
suppress(constructor(EvilBase.class));
suppress(constructor(NicerSingleton.class));
assertEquals(42, NicerSingleton.getInstance().theMethod());
}
}
Комментарии:
1. Эта аннотация ничего не изменила. Я пытался использовать ClassToBeTested.class и MySingleton.class , ни один из которых, казалось, никак не изменил результат.
2. @user1042480 Какая версия PowerMock?
3. Используя powermock-easymock-1.4.10-full с easymock-3.1. Java 1.6
4. Я немного изменил метод тестирования, и это в сочетании с предлагаемыми вами аннотациями, похоже, сделало свое дело. Аннотации определенно были частью проблемы, так что спасибо за помощь!
5. @user1042480 Кстати, я не уверен, что вам нужно подавлять оба ctor-а; Я думаю, что если вы подавите ctor суперкласса, базовый не будет вызван.
Ответ №2:
Как насчет того, чтобы задать поле экземпляра ( 'only'
в вашем коде) вашего синглтона с помощью экземпляра, созданного с помощью нужного вам конструктора (вы можете сделать все это с помощью Reflection API или dp4j).
Это обсуждается в мотивирующем примере публикации dp4j.
Ответ №3:
Я не уверен, что вы делаете неправильно. Но со стороны дизайна я могу предложить вам изучить внедрение зависимостей, то есть DI.
Чтобы сделать ваш код тестируемым, используйте DI . С помощью DI вы бы передали одноэлементный класс в качестве аргумента конструктора вашему тестовому классу. И теперь, поскольку вы передаете аргумент, внутри вашего тестового примера вы можете создать пользовательскую реализацию AbstractSingleton
класса, и ваш тестовый пример должен работать нормально.
С DI ваш код станет более тестируемым.
Комментарии:
1. Спасибо за совет, но это не вариант.