подавлять одноэлементный конструктор в java с помощью powermock

#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. Спасибо за совет, но это не вариант.