Как издеваться над финальными классами и иметь покрытие кода - PullRequest
0 голосов
/ 26 ноября 2018

Мне не удается найти решение моих проблем с JUnit, поэтому я попытался максимально упростить это, поэтому я надеюсь, что это будет легко понять.

По сути, я пытаюсь проверить этокласс:

public class PB {
    public int startProcessBuilder() {
        int status = 1;
        try {
            ProcessBuilder pb = new ProcessBuilder("java", "-jar", ".....");
            pb.directory(new File("/directory"));
            Process process = pb.start();
            status = process.waitFor();
        } catch (IOException | InterruptedException e) {
            System.out.println(e.getMessage());
        }
        return status;
    }
}

Итак, я придумал этот тест:

@RunWith(PowerMockRunner.class)
@PrepareForTest({ ProcessBuilder.class, PB.class })
public class PBTest {

    private PB spyInstance = Mockito.spy(PB.class);
    private ProcessBuilder processBuilderMock = PowerMockito.mock(ProcessBuilder.class);
    private Process processMock = Mockito.mock(Process.class);

    @Before
    public void initialize() throws Exception {
        PowerMockito.whenNew(ProcessBuilder.class).withParameterTypes(String[].class).withArguments(anyVararg())
                .thenReturn(processBuilderMock);
        PowerMockito.doReturn(processMock).when(processBuilderMock).start();
    }

    @Test
    public void testStartProcessBuilder() throws Exception {
        assertThat(spyInstance.startProcessBuilder(), is(0));
    }
}

Я знаю, что мой тест проходит успешно, но в компании, в которой я работаю, мы используем jacoco иeclemma для отображения покрытия кода. Известно, что весь код отображается как покрытие 0%, если класс, который мы тестируем, содержится в аннотации @PrepareForTest.

Итак, существует известное решение, которым мы сейчас занимаемсяиспользуя некоторое время, используя MockitoJUnitRunner (http://www.notonlyanecmplace.com/make-eclemma-test-coverage-work-with-powermock/)

@RunWith(MockitoJUnitRunner.class)
@PrepareForTest({ ProcessBuilder.class, PB.class })
public class PBTest {

    private PB spyInstance = Mockito.spy(PB.class);
    private ProcessBuilder processBuilderMock = PowerMockito.mock(ProcessBuilder.class);
    private Process processMock = Mockito.mock(Process.class);

    @Rule
    public PowerMockRule rule = new PowerMockRule();

    static {
        PowerMockAgent.initializeIfNeeded();
    }

    @Before
    public void initialize() throws Exception {
        PowerMockito.whenNew(ProcessBuilder.class).withParameterTypes(String[].class).withArguments(anyVararg())
                .thenReturn(processBuilderMock);
        PowerMockito.doReturn(processMock).when(processBuilderMock).start();
    }

    @Test
    public void testStartProcessBuilder() throws Exception {
        assertThat(spyInstance.startProcessBuilder(), is(0));
    }
}

Теперь возникают реальные проблемы: когда я пытаюсь запустить свой тест, появляется это исключение: org.mockito.exceptions.misusing.NotAMockException:Аргумент передается, когда () не является ложным!NES

private ProcessBuilder processBuilderMock = PowerMockito.mock(ProcessBuilder.class);

PowerMockito.doReturn(processMock).when(processBuilderMock).start();

этим:

private ProcessBuilder processBuilderMock = Mockito.mock(ProcessBuilder.class);

PowerMockito.doReturn(processMock).when(processBuilderMock).start();

но затем, конечно: Cannot mock/spy class java.lang.ProcessBuilder... because it is a final class (вероятно, почему я использовал PowerMock в первую очередь)

Что такоемои варианты?

1 Ответ

0 голосов
/ 26 ноября 2018

Вы можете создать класс PB, чтобы его можно было легко тестировать.Один из способов сделать это - извлечь ProcessBuilder параметры:

public class PB {
  public int startProcessBuilder(String... args) {
    try {
      ProcessBuilder pb = new ProcessBuilder(args);

Позже в тесте вы можете использовать небольшой тестовый JAR "Hello World":

new PB().startProcessBuilder("java", "-jar", "path-to-test-jar");

или использоватьстандартная команда echo, которая должна иметь одинаковый синтаксис независимо от операционной системы:

new PB().startProcessBuilder("echo", "Hello", "World");

вам не нужно ничего подделывать, и вы фактически вызываете поддельный процесс Java с помощью поддельного JAR.

Тот факт, что вы собираетесь приложить столько усилий, чтобы увеличить освещение, делает ваш текущий процесс разработки сомнительным.Покрытие - это не самоцель, а показатель, который должен дать вам уверенность в коде.Если вам нужно повысить его, избегая @PrepareForTest, который хорошо работает, какой смысл?

...