Работа с System.exit(0) в тестах JUnit

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

Я запускаю приложение заново в каждом тесте, помещая каждый тестовый метод в отдельный тестовый пример и используя параметр fork="yes" в задаче Ant junit. Однако некоторые из вариантов использования, которые я хотел бы реализовать в качестве тестов, включают пользователя, выходящего из приложения, что приводит к одному из методов, вызывающих System.exit(0). Это рассматривается JUnit как ошибка: junit.framework.AssertionFailedError: Forked Java VM exited abnormally.

Есть ли способ сказать JUnit, что выход с нулевым кодом возврата на самом деле нормально?

Ответ 1

Как я справляюсь с этим - установить диспетчера безопасности, который выдает исключение при вызове System.exit. Тогда есть код, который ловит исключение и не прерывает проверку.

public class NoExitSecurityManager
    extends java.rmi.RMISecurityManager
{
    private final SecurityManager parent;

    public NoExitSecurityManager(final SecurityManager manager)
    {
        parent = manager;
    }

    public void checkExit(int status)
    {
        throw new AttemptToExitException(status);
    }

    public void checkPermission(Permission perm)
    {
    }
}

И затем в коде что-то вроде:

catch(final Throwable ex)
{
    final Throwable cause;

    if(ex.getCause() == null)
    {
        cause = ex;
    }
    else
    {
        cause = ex.getCause();
    }

    if(cause instanceof AttemptToExitException)
    {
        status = ((AttemptToExitException)cause).getStatus();
    }
    else
    {
        throw cause;
    }
}

assertEquals("System.exit must be called with the value of " + expectedStatus, expectedStatus, status);

Ответ 2

В библиотеке System Rules есть правило JUnit, называемое ExpectedSystemExit. С помощью этого правила вы можете протестировать код, который вызывает System.exit(...):

public class MyTest {
    @Rule
    public final ExpectedSystemExit exit = ExpectedSystemExit.none();

    @Test
    public void systemExitWithArbitraryStatusCode() {
        exit.expectSystemExit();
        /* the code under test, which calls System.exit(...)
         * with an arbitrary status
         */
    }

    @Test
    public void systemExitWithSelectedStatusCode0() {
        exit.expectSystemExitWithStatus(0);
        //the code under test, which calls System.exit(0)
    }
}

Системные правила нуждаются, по крайней мере, в JUnit 4.9.

Полное раскрытие: я являюсь автором Системных правил.

Ответ 3

Не могли бы вы абстрагироваться от "выхода системы" в новую зависимость, чтобы в ваших тестах вы могли просто иметь подделку, которая записывает тот факт, что вызывается вызов (и значение), но использует реализацию, которая вызывает System.exit в реальном приложении?