Как издеваться как над статическими, так и над нестатическими методами в одном классе, используя PowerMock?

#java #unit-testing #easymock #powermock

#java #модульное тестирование #easymock #powermock

Вопрос:

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

Представьте набор классов следующим образом :

  • Класс утилиты :
 package org.osef.test;

public final class A {

    private static A instance;
    public static String status;

    private A() {
        initPaths();
    }

    public static A getInstance(){
            if(instance==null){
                instance = new A();
            }
            return instance;
    }

    private void initPaths() {
        A.status = "I have been in the method !";
    }
    public String doStuff() {
        return "stuff done ...";
    }
}
  
  • вызывающий его класс
 package org.osef.test;

public class B {

    public String doBdo() {
        A instance = A.getInstance();
        return instance.doStuff();
    }
}
  
  • класс, тестирующий эту кучу дерьма … кхм … чрезвычайно сложная часть «логики».

package org.osef.test;

 import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;

import org.easymock.EasyMock;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.api.easymock.PowerMock;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import org.powermock.reflect.Whitebox;

@RunWith(PowerMockRunner.class)
@PrepareForTest({ A.class })
public class BTest {

    @Before
    public void setUp() {
        PowerMock.replace(PowerMock.method(A.class, "getInstance")).with(PowerMock.method(BTest.class, "giveOutInstance"));

        A a = A.getInstance();

        EasyMock.expect(a.doStuff()).andReturn("lol");
        EasyMock.replay(a);
    }

    @Test
    public void testDoBdo() {

        B b = new B();
        assertEquals("lol", b.doBdo());
        assertNull(A.status);
    }

    public static A giveOutInstance(){
        return Whitebox.newInstance(A.class);
    }
}
  
  • и другой подход уже заключался в следующем :
 package org.osef.test;

//[imports ommited here but are the same that those of the previous example]

@RunWith(PowerMockRunner.class)
@PrepareForTest({ A.class })
public class BTest {

    @Before
    public void setUp() {
        PowerMock.mockStatic(A.class);
        A a = Whitebox.newInstance(A.class);
        EasyMock.expect(A.getInstance()).andReturn(a);
        PowerMock.replay(A.class);

        EasyMock.expect(a.doStuff()).andReturn("lol");

        EasyMock.replay(a);
    }

    @Test
    public void testDoBdo() {

        B b = new B();
        assertEquals("lol", b.doBdo());
        assertNull(A.status);
    }

}
  

Но во всех случаях я получаю :

java.lang.Исключение IllegalStateException: нет последнего вызова для макета, доступного в org.easymock.EasyMock.getControlForLastCall(EasyMock.java:560) в org.easymock.EasyMock.expect(EasyMock.java:538) в org.osef.test.BTest.setUp(BTest.java:25) …

  • Мне просто нужно протестировать этот последний класс A.
  • Мне нужно избегать логики конструктора (чудовищной и неуместной в моем тестировании метода «doStuff»).
  • Я должен протестировать этот doStuff.

Есть идеи, как эффективно делать то, что я хочу делать?

Ответ №1:

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

PowerMock.replayAll() твой друг здесь. Это приведет к тому, что классы, от которых вы ожидаете, будут находиться в режиме воспроизведения, тогда ваш макет экземпляра может быть возвращен вызовом статического метода.

Вот пример теста, который я подготовил для вашего примера:

 import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;

import org.easymock.EasyMock;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.api.easymock.PowerMock;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;

@RunWith(PowerMockRunner.class)
@PrepareForTest(A.class)
public class BTest{

    @Test
    public void thatCallingClassMakesExpectedCalls() {
        final A mockA = PowerMock.createMock(A.class);
        EasyMock.expect(mockA.doStuff()).andReturn("lol").anyTimes();

        PowerMock.mockStatic(A.class);
        EasyMock.expect(A.getInstance()).andReturn(mockA).anyTimes();

        PowerMock.replayAll(mockA);

        final B callingClass = new B();
        final String doBdo = callingClass.doBdo();
        assertThat(doBdo, is("lol"));

        EasyMock.verify(mockA);
        PowerMock.verifyAll();
    }
}
  

Комментарии:

1. Большое вам спасибо! Это работает нормально, теперь я попытаюсь выяснить, почему это так, поэтому я собираюсь углубиться в документацию, но благодаря вам я знаю, в какую сторону смотреть: D

2. Не беспокойтесь! Недавно я сам познал прелести PowerMock, поэтому мне будет интересно услышать, в чем вы в конечном итоге определяете свою проблему.

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