2013-04-13 3 views
1

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

Проблема, с которой я столкнулся, заключается в следующем: как только код, который я пытаюсь проверить, завершает свою работу, его вызывающий System.exit(), который закрывает приложение. Хотя он также останавливает мои тесты от завершения, так как он закрывает JVM (я полагаю).

Нужно ли вообще обойти эту проблему без изменения исходного кода? Первоначально я пытался запустить тестирование приложения im из нового потока, хотя это, очевидно, не имело большого значения.

+0

Можем ли мы узнать, какая библиотека это? – TheEwook

+3

Приложение Java ** ** может вызывать System.exit(), библиотеку Java ** ** не следует! – Aubin

+1

JUnit лучше всего подходит для тестирования классов библиотек, которые принимают предсказуемый тип данных в качестве их ввода и возвращают предсказуемый тип данных в качестве своего вывода. Класс библиотеки никогда не должен вызывать 'System.exit()', потому что это делает библиотеку вредной для приложений, которые могут ее использовать. Похоже, вы пытаетесь использовать JUnit для тестирования небиблиотечных классов или методов, и это не очень удобно. – Bobulous

ответ

7

Вы можете использовать System Rules: «Коллекция правил JUnit для тестирования кода, который использует java.lang.System».

Среди их правил у вас есть ExpectedSystemExit, ниже приведен пример того, как его использовать. Я считаю, что это очень чистое решение.

import org.junit.Rule; 
import org.junit.Test; 
import org.junit.contrib.java.lang.system.Assertion; 
import org.junit.contrib.java.lang.system.ExpectedSystemExit; 

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

    @Test 
    public void noSystemExit() { 
     //passes 
    } 

    @Test 
    public void executeSomeCodeAFTERsystemExit() { 
     System.out.println("This is executed before everything."); 
     exit.expectSystemExit(); 
     exit.checkAssertionAfterwards(new Assertion() { 
      @Override 
      public void checkAssertion() throws Exception { 
       System.out.println("This is executed AFTER System.exit()"+ 
       " and, if exists, the @org.junit.After annotated method!"); 
      } 
     }); 
     System.out.println("This is executed right before System.exit()."); 
     System.exit(0); 
     System.out.println("This is NEVER executed."); 
    } 

    @Test 
    public void systemExitWithArbitraryStatusCode() { 
     exit.expectSystemExit(); 
     System.exit(0); 
    } 

    @Test 
    public void systemExitWithSelectedStatusCode0() { 
     exit.expectSystemExitWithStatus(0); 
     System.exit(0); 
    } 

    @Test 
    public void failSystemExit() { 
     exit.expectSystemExit(); 
     //System.exit(0); 
    } 

} 

Если вы используете Maven, вы можете добавить к вашей pom.xml:

<dependency> 
    <groupId>junit</groupId> 
    <artifactId>junit</artifactId> 
    <version>4.11</version> 
</dependency> 
<dependency> 
    <groupId>com.github.stefanbirkner</groupId> 
    <artifactId>system-rules</artifactId> 
    <version>1.3.0</version> 
</dependency> 
+0

Я не уверен, правильно ли я использую его, но приведенный выше код ничего не делает. У меня есть exit.expectSystemExit() перед вызовом класса, вызывающего System.exit (0), и приложение все еще закрывается. Моя цель - предотвратить закрытие приложения (включая тесты JUnit). – Giannis

+1

@ Giannis Я вижу, вы хотите выполнить какой-то код ** после вызова 'System.exit()', правильно? Я отредактировал ответ и добавил пример (см. Метод 'executeSomeCodeAFTERsystemExit()'). Еще самое чистое решение для меня. – acdcjunior

+0

@ Giannis Я должен указать, что это решение является оптимальным, как код 'System.out.println (« Это выполняется AFTER System.exit()! »);' ** ** ** выполняется **, если ** Вызывается 'System.exit (0)'! Кроме того, это решение в JUnit, и оно не меняет никакого (общесистемного) поведения вне его. – acdcjunior

-2

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

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

+0

Возможно, правильно, но я не уверен. Я думаю, вы можете издеваться над статическими и окончательными методами с помощью powermock. Не пробовал, поэтому я не знаю точно. – emory

+0

@bluevoid Неверно, вы можете использовать SecurityManager, как указано выше, и в повторяющихся вопросах. –

+1

нормально, подставка. Я не знал, что можно перехватить этот звонок. – bluevoid

2

System.exit(status) собственно делегатов призыв к Время воспроизведения класса. время выполнения перед выполнением этого запроса вызывает выключение checkExit(status) на JVM в текущем SecurityManager который может предотвратить надвигающийся остановку, бросая SecurityException.

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

В вашем JUnit тестовый класс, настроить SecurityManager в setUP() методе:

securityManager = System.getSecurityManager(); 
    System.setSecurityManager(new SecurityManager() { 
     @Override 
     public void checkExit(int status) { 
      super.checkExit(status); // This is IMPORTANT! 
      throw new SecurityException("Overriding shutdown..."); 
     } 
    }); 

В tearDown() заменить SecurityManager снова с экземпляром, который мы сохранили раньше. Несоблюдение этого означает, что JUnit не выключится! :)

Ссылки:
http://docs.oracle.com/javase/1.5.0/docs/api/java/lang/SecurityManager.html
http://docs.oracle.com/javase/1.5.0/docs/api/java/lang/SecurityManager.html#checkExit(int)

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

 SecurityManager security = System.getSecurityManager(); 
    if (security != null) { 
     security.checkXXX(argument, . . .); 
    } 

Менеджер безопасности, таким образом, предоставлена ​​возможность предотвратить завершение операции, бросая исключение. Процедура диспетчера безопасности просто возвращается, если операция разрешена, но выдает исключение SecurityException, если операция не разрешена.

+0

Большое спасибо за ваш отличный ответ. Я скоро проверю его. – Giannis

+1

Должен был добавить (ожидается = SecurityException.class) перед тестами и работать отлично. – Giannis

+0

Да, вместо того, чтобы ловить его, @Test (expected = SecurityException.class) 'здесь имеет смысл. Спасибо, за ответ. –

Смежные вопросы