подавить одноэлементный конструктор в Java с Powermock - PullRequest
3 голосов
/ 12 ноября 2011

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

Мое понимание способа решить эту проблему будет выглядеть примерно так:

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. Несмотря на осуждение, я тоже пытался, но безрезультатно ...

Возможно ли то, что я пытаюсь сделать? Если так, что я делаю не так?

* Отредактированная версия теперь работает.

Ответы [ 3 ]

1 голос
/ 12 ноября 2011

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

Кроме того, сигнатура подавления ctor суперкласса должна включать класс somevar;сейчас вы просто подавляете ctor по умолчанию.

См. @PrepareForTest API документы. Вот сообщение в блоге с некоторыми дополнительными деталями.

FWIW, это работает для меня:

@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());
    }

}
0 голосов
/ 18 ноября 2011

Как насчет того, чтобы установить поле экземпляра ('only' в вашем коде) вашего Singleton с экземпляром, созданным с помощью нужного конструктора (вы можете сделать все это с помощью API Reflection или dp4j ).

Мотивирующий пример публикации dp4j обсуждает это.

0 голосов
/ 12 ноября 2011

Я не уверен, что ты делаешь неправильно. Но на стороне дизайна, я могу предложить вам изучить внедрение зависимости, т.е. DI.

Чтобы сделать ваш код тестируемым, используйте DI. С DI вы передавали бы синглтон-класс в качестве аргумента конструктора вашему тестовому классу. И теперь, когда вы передаете аргумент, внутри вашего тестового примера вы можете создать собственную реализацию класса AbstractSingleton, и ваш тестовый пример должен работать нормально.

С DI ваш код станет более тестируемым.

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