Как издеваться над вызовом kotlin.system.exitProcess

Я хочу протестировать некоторый код, который использует сторонний код, который вызывает kotlin.system.exitProcess()определяется следующим образом в стандартной библиотеке lib:

@kotlin.internal.InlineOnly
public inline fun exitProcess(status: Int): Nothing {
    System.exit(status)
    throw RuntimeException("System.exit returned normally, while it was supposed to halt JVM.")
}

когда exitProcess() называется, JVM останавливается и дальнейшее тестирование невозможно. Мне не удалось высмеять звонки exitProcess() с макетом. Является ли это возможным?

Некоторая дополнительная информация:

Сторонней библиотекой является Clikt ( https://ajalt.github.io/clikt/), отличная библиотека для создания интерфейса командной строки. Приложение Clikt анализирует командную строку и завершает работу в случае сбоя. Это может быть одной из редких причин, когда вызывается System.exit. Конечно, есть более тестируемые решения, но в любом случае, при работе со сторонними библиотеками, спорить о том, что может быть лучше сделано в lib, устарело.

На самом деле я хочу проверить, что мое приложение записывает ожидаемое сообщение об использовании при вызове с --help или неправильными аргументами.

Я также пытался издеваться над вызовом System.exit() таким образом: mockkStatic("java.lang.System") каждые { System.exit(any()) }.throws(RuntimeException("blubb"))), что приводит к другой проблеме, так как все вызовы System поддразниваются тогда:

io.mockk.MockKException: every/verify {} block were run several times. Recorded calls count differ between runs
Round 1: class java.lang.System.getProperty(kotlin.ignore.old.metadata), class java.lang.System.exit(-630127373)
Round 2: class java.lang.System.exit(158522875)

Как ни странно, мне удалось проверить это на Java с помощью jmockit, например так:

public class MainTestJ {
    private ByteArrayOutputStream consoleOut;

    @BeforeEach
    public void mockConsole() {
        consoleOut = new ByteArrayOutputStream();
        System.setOut(new PrintStream(consoleOut));
    }

    @Test
    public void can_mock_exit() {
        new MockUp<System>() {
            @Mock
            public void exit(int exitCode) {
                System.out.println("exit called");
            }
        };
        assertThatThrownBy(() -> {
            new Main().main(new String[] { "--help" });
        }).isInstanceOf(RuntimeException.class);
        assertThat(consoleOut.toString()).startsWith("Usage: bla bla ...");
    }
}

1 ответ

Я играл с этим некоторое время, пытаясь заставить его работать, но, к сожалению, это невозможно. Есть несколько трудностей с насмешкой над этой функцией; что это функция верхнего уровня и что она возвращает Nothing, Каждый из них может быть преодолен, но что делает невозможным то, что функция встроена. Встроенные функции kotlin не производят методы в байт-коде, это, как сказано в олове, встроено. Проблема в том, что Mockk и другие библиотеки-насмешки используют инструкции для байт-кода. Обратитесь к этой проблеме для получения дополнительной информации.

Ваша лучшая альтернатива - вообще не пытаться имитировать эту функцию, а вместо этого смоделировать вызов в сторонней библиотеке, которую вы используете. В конце концов, вы не должны тестировать этот сторонний код, только свой собственный код. А еще лучше, может быть, вам следует искать альтернативную библиотеку. Как уже было сказано в комментариях, сторонняя библиотека не должна выходить из процесса, это следует оставить на усмотрение клиентского кода.

Другие вопросы по тегам