Как мне смоделировать приватное статическое конечное поле, инициализированное через приватный конструктор, используя Powermock и Mockito?

Вот мой исходный класс -

public class ClassToTest extends AbstractSuperClass<Integer> {
    private static final ClassToTest INSTANCE = new ClassToTest(); // (line 1) need to mock this variable

    static ClassToTest get() {
        return INSTANCE;
    }
    private ClassToTest() {
        super(Integer.class);// (line 2)
    }
}

Вот моя попытка до сих пор проверить его

@RunWith(PowerMockRunner.class)
@PrepareForTest(ClassToTest.class)
public class TestClass {
    private ClassToTest testClass;
    @Before
    public void setUp() {
        // each of the below attempts fails at line 1  because of the call to line 2 (annotated above).
        // Attempt A.  
        testClass = WhiteBox.newInstance(ClassToTest.class);
        //Attempt B.
        testClass = mock(ClassToTest.class);
        WhiteBox.setInternalState(ClassToTest.class, "INSTANCE", testClass);
    }
    @Test
    public void dummy() {
        // irrelevant
    }
}

Я пытаюсь эффективно издеваться ClassToTest.INSTANCE и вызов его частного конструктора. Как я мог это сделать?

РЕДАКТИРОВАТЬ: фрагмент / конструктор вызывается из AbstractSuperClass,

public abstract class AbstractSuperClass<V extends Serializable> {
    private final CacheClient<V> cache;
    private final int seconds;

    public AbstractSuperClass(Class<V> valueType) {
        cache = new ClientFactory(Config.getAppConfig(), StatisticSet.getGlobalStatistics()).newClient(getCacheType(), valueType);
        seconds = Config.getAppConfig().get(getCacheType().getSectionEnum()).getSeconds();
    }

PS: я пытаюсь держаться подальше от внутренних дел AbstractSuperClass и первоначально надеялся просто издеваться над вызовом. Я также открыт для любых идей по рефакторингу ClassToTest чтобы избежать этого.

3 ответа

Ваша настоящая проблема в том, что вы создали сложный, почти не тестируемый код, используя статический. И не только это: вы также создаете плохой дизайн. Потому что вы тесно связываете свои производственные классы друг с другом. После этого будет трудно избавиться от этого статического метода в дальнейшем.

Итак, другой вариант: вместо того, чтобы пытаться "исправить" сломанный дизайн с помощью PowerMock, вы отступаете. Вы узнаете, что такое "написание тестируемого" кода (например, просмотрев эти видео); а затем вы используете чистые интерфейсы и внедрение зависимостей для решения вашей проблемы. И вы тестируете все это с EasyMock или Mockito, без необходимости Powermock!

Я не понимаю, чего вы пытаетесь достичь, но это работает здесь:

@PrepareForTest(ClassToTest.class) // required
@RunWith(PowerMockRunner.class)    // required
public class ClassToTestTest {

    private ClassToTest testClass;

    @Before
    public void setUp() throws Exception {
        this.testClass = Mockito.mock(ClassToTest.class);
        final Field instance = ClassToTest.class.getDeclaredField("INSTANCE");
        instance.setAccessible(true);
        instance.set(null, this.testClass);
    }

    @Test
    public void testGet() throws Exception {
        assertSame(this.testClass, ClassToTest.get());
        System.out.println(this.testClass);
    }
}

Выход:

Mock for ClassToTest, hashCode: 1083021083

(Протестировано с помощью powermock-api-mockito версии 1.6.2)

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

Вы должны издеваться ClassToTest.get() вместо следующего

@RunWith(PowerMockRunner.class)
@PrepareForTest(ClassToTest.class)
public class TestClass {
    private ClassToTest testClass;
    @Before
    public void setUp() {
        testClass = PowerMockito.mock(ClassToTest.class);
        PowerMockito.mockStatic(ClassToTest.class);
        Mockito.when(ClassToTest.get()).thenReturn(testClass);
    }

    @Test
    public void dummy() {
        // Here I get the instance of ClassToTest that I mocked in the setUp method
        System.out.println(ClassToTest.get());
    }
}
Другие вопросы по тегам