Java - Mock private stati c конструктор для нескольких тестов - PullRequest
0 голосов
/ 02 апреля 2020

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

public class Bar {
    public Object doSomething(int a, String b){
        return "something";
    }

    public Object doSomethingElse(int a, int b, String c){
        return "something else";
    }
}
public class Foo {
    private static Bar bar = new Bar();

    public static void start(int a, int b, String c){
        if(a == 1) { // some calculated condition
            bar.doSomething(a, c);
        } else {
            bar.doSomethingElse(a, b, c);
        }
    }
}
@RunWith(PowerMockRunner.class)
@PrepareForTest(Foo.class)
public class FooTest {
    @Test
    public void somethingTest() throws Exception {
        Bar barMock = createMock(Bar.class);

        expectNew(Bar.class).andReturn(barMock);

        expect(barMock.doSomething(1, "xxx")).andReturn("ABC");

        replay(barMock, Bar.class);

        Foo.start(1, 2, "xxx");
        verify(barMock, Bar.class);
    }

    @Test
    public void somethingElseTest() throws Exception {
        Bar barMock = createMock(Bar.class);

        expectNew(Bar.class).andReturn(barMock);

        expect(barMock.doSomethingElse(0, 2,"xxx")).andReturn("ABC");

        replay(barMock, Bar.class);

        Foo.start(0, 2, "xxx");
        verify(barMock, Bar.class);
    }
}

Выполнение тестов индивидуально работа, но не весь класс, я думаю, что это связано с:

 private static Bar bar = new Bar();

, но я не уверен на 100%. В любом случае, если я не могу изменить классы foo / bar, как я могу решить эту проблему?

Ответы [ 2 ]

0 голосов
/ 02 апреля 2020

Проблема, с которой вы сталкиваетесь, заключается в том, что поле static поля class инициализируется только один раз. Следовательно, первый тест создает новый объект Bar, но этот макет сохраняется во всех остальных тестах, так как новое создание не запускается.

Вместо этого следует использовать Reflections, чтобы установить поле private static для каждого теста , PowerMock предоставляет вам служебный класс с именем Whitebox.

Также обратите внимание, что setInternalState работает только в том случае, если в этом классе есть ровно один экземпляр объекта Bar. Вы должны полагаться на обычные Java классы отражения, если их будет несколько.

Пример см. Ниже:

@RunWith(PowerMockRunner.class)
@SuppressStaticInitializationFor("test.Foo") // optional, only required if the Bar class could not be created normally.
public class StaticTest {

    @Test
    public void somethingTest() throws Exception {
        Bar barMock = createMock(Bar.class);
        Whitebox.setInternalState(Foo.class, barMock);

        expect(barMock.doSomething(1, "xxx")).andReturn("ABC");

        replay(barMock, Bar.class);

        Foo.start(1, 2, "xxx");
        verify(barMock, Bar.class);
    }

    @Test
    public void somethingElseTest() throws Exception {
        Bar barMock = createMock(Bar.class);
        Whitebox.setInternalState(Foo.class, barMock);

        expect(barMock.doSomethingElse(0, 2,"xxx")).andReturn("ABC");

        replay(barMock, Bar.class);

        Foo.start(0, 2, "xxx");
        verify(barMock, Bar.class);
    }
}

Я объединил это с подавлением состояния c инициализация, но строго говоря, это не обязательно для вашего теста, так как создание класса Bar завершается успешно независимо.

Аннотация должна использоваться только в случае, если класс Bar не может быть создан. Обратите внимание, что вам нужно будет ввести полностью квалифицированное имя, включая имя пакета (я предположил, что пакет будет test).

0 голосов
/ 02 апреля 2020

Я бы реорганизовал Foo как:

public class Foo {
    private static Bar bar = new Bar();

    public static void start(int a, int b, String c){
        if(a == 1) { // some calculated condition
            doSomething(a, c);
        } else {
            doSomethingElse(a, b, c);
        }
    }

    private static void doSomething(int a, String c) {
        bar.doSomething(a, c);
    }

    private static void doSomethingElse(int a, int b, String c) {
        bar.doSomethingElse(a, b, c);
    }
}

И протестировал бы Foo как:

import static org.mockito.Matchers.eq;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.AdditionalMatchers;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;


@RunWith(PowerMockRunner.class)
@PrepareForTest({Foo.class})
public class FooTest {

    @Before
    public void setup() throws Exception {
        PowerMockito.spy(Foo.class);
    }


    @Test
    public void somethingTest() throws Exception {
        Foo.start(1, 2, "xxx");   
        PowerMockito.verifyPrivate(Foo.class).invoke("doSomething", eq(1), eq("xxx"));
    }

    @Test
    public void somethingElseTest() throws Exception {
        Foo.start(0, 2, "xxx"); 
        PowerMockito.verifyPrivate(Foo.class).invoke("doSomethingElse", AdditionalMatchers.not(eq(1)), eq(2), eq("xxx"));
    }
}

И отдельно протестировал Bar, что можно сделать просто с помощью Mockito.

...