Java : System.exit ()를 호출하는 메소드를 테스트하는 방법?
System.exit()
특정 입력을 호출 해야하는 몇 가지 방법이 있습니다. 불행히도, 이러한 경우를 테스트하면 JUnit이 종료됩니다! 메소드 호출을 새 스레드에 넣는 것은 System.exit()
현재 스레드뿐만 아니라 JVM을 종료하기 때문에 도움이되지 않는 것 같습니다 . 이 문제를 처리하는 데 일반적인 패턴이 있습니까? 예를 들어, 스텁을 대체 할 수 System.exit()
있습니까?
[편집] 문제의 클래스는 실제로 JUnit 내에서 테스트하려는 명령 행 도구입니다. 아마도 JUnit이 그 일에 적합한 도구가 아닐 수도 있습니다. 보완 회귀 테스트 도구에 대한 제안을 환영합니다 (JUnit 및 EclEmma와 잘 통합 된 것).
실제로 Derkeiler.com 은 다음 과 같이 제안합니다.
- 왜
System.exit()
?
System.exit (whateverValue)로 종료하는 대신 검사되지 않은 예외를 발생시키지 않는 이유는 무엇입니까? 정상적인 사용에서는 JVM의 마지막 도랑 포수로 표류하고 스크립트를 종료합니다 (어딘가에 어딘가에 스크립트를 잡기로 결정하지 않는 한 언젠가는 유용 할 수 있습니다).
JUnit 시나리오에서는 JUnit 프레임 워크에 의해 포착됩니다. JUnit 프레임 워크는 이러한 테스트가 실패했다고보고하고 다음 단계로 원활하게 이동합니다.
- 방지는
System.exit()
실제로 JVM을 종료합니다 :
System.exit 호출을 방해하는 보안 관리자로 실행되도록 TestCase를 수정 한 다음 SecurityException을 포착하십시오.
public class NoExitTestCase extends TestCase
{
protected static class ExitException extends SecurityException
{
public final int status;
public ExitException(int status)
{
super("There is no escape!");
this.status = status;
}
}
private static class NoExitSecurityManager extends SecurityManager
{
@Override
public void checkPermission(Permission perm)
{
// allow anything.
}
@Override
public void checkPermission(Permission perm, Object context)
{
// allow anything.
}
@Override
public void checkExit(int status)
{
super.checkExit(status);
throw new ExitException(status);
}
}
@Override
protected void setUp() throws Exception
{
super.setUp();
System.setSecurityManager(new NoExitSecurityManager());
}
@Override
protected void tearDown() throws Exception
{
System.setSecurityManager(null); // or save and restore original
super.tearDown();
}
public void testNoExit() throws Exception
{
System.out.println("Printing works");
}
public void testExit() throws Exception
{
try
{
System.exit(42);
} catch (ExitException e)
{
assertEquals("Exit status", 42, e.status);
}
}
}
2012 년 12 월 업데이트 :
윌 제안 코멘트에 사용하는 시스템 규칙 (4.9+) 규정 된 사용하는 코드를 테스트하기 위해, JUnit을의 컬렉션을 java.lang.System
.
이것은 처음에 언급 한 스테판 버크 너 에 그의 대답 2011 년 12 월.
System.exit(…)
ExpectedSystemExit
규칙을 사용하여System.exit(…)
호출 되었는지 확인 하십시오 .
종료 상태도 확인할 수 있습니다.
예를 들어 :
public void MyTest {
@Rule
public final ExpectedSystemExit exit = ExpectedSystemExit.none();
@Test
public void noSystemExit() {
//passes
}
@Test
public void systemExitWithArbitraryStatusCode() {
exit.expectSystemExit();
System.exit(0);
}
@Test
public void systemExitWithSelectedStatusCode0() {
exit.expectSystemExitWithStatus(0);
System.exit(0);
}
}
라이브러리 시스템 규칙 에는 ExpectedSystemExit라는 JUnit 규칙이 있습니다. 이 규칙을 사용하면 System.exit (...)를 호출하는 코드를 테스트 할 수 있습니다.
public void MyTest {
@Rule
public final ExpectedSystemExit exit = ExpectedSystemExit.none();
@Test
public void systemExitWithArbitraryStatusCode() {
exit.expectSystemExit();
//the code under test, which calls System.exit(...);
}
@Test
public void systemExitWithSelectedStatusCode0() {
exit.expectSystemExitWithStatus(0);
//the code under test, which calls System.exit(0);
}
}
전체 공개 : 저는 그 도서관의 저자입니다.
이 메소드에 "ExitManager"를 주입하는 방법은 다음과 같습니다.
public interface ExitManager {
void exit(int exitCode);
}
public class ExitManagerImpl implements ExitManager {
public void exit(int exitCode) {
System.exit(exitCode);
}
}
public class ExitManagerMock implements ExitManager {
public bool exitWasCalled;
public int exitCode;
public void exit(int exitCode) {
exitWasCalled = true;
this.exitCode = exitCode;
}
}
public class MethodsCallExit {
public void CallsExit(ExitManager exitManager) {
// whatever
if (foo) {
exitManager.exit(42);
}
// whatever
}
}
프로덕션 코드는 ExitManagerImpl을 사용하고 테스트 코드는 ExitManagerMock을 사용하며 exit ()가 호출되었는지 여부와 종료 코드를 확인할 수 있습니다.
실제로 JUnit 테스트에서 메소드를 조롱하거나 스텁 아웃 할 수 있습니다System.exit
.
예를 들어, JMockit 을 사용 하면 다음과 같이 작성할 수 있습니다 (다른 방법도 있음).
@Test
public void mockSystemExit(@Mocked("exit") System mockSystem)
{
// Called by code under test:
System.exit(); // will not exit the program
}
편집 : 호출 후 코드를 실행할 수없는 대체 테스트 (최신 JMockit API 사용) System.exit(n)
:
@Test(expected = EOFException.class)
public void checkingForSystemExitWhileNotAllowingCodeToContinueToRun() {
new Expectations(System.class) {{ System.exit(anyInt); result = new EOFException(); }};
// From the code under test:
System.exit(1);
System.out.println("This will never run (and not exit either)");
}
코드베이스에서 사용한 한 가지 트릭은 System.exit ()에 대한 호출을 Runnable impl에 캡슐화하는 것입니다. 문제의 메소드는 기본적으로 사용됩니다. 단위 테스트를 위해 다른 모의 Runnable을 설정했습니다. 이 같은:
private static final Runnable DEFAULT_ACTION = new Runnable(){
public void run(){
System.exit(0);
}
};
public void foo(){
this.foo(DEFAULT_ACTION);
}
/* package-visible only for unit testing */
void foo(Runnable action){
// ...some stuff...
action.run();
}
... 그리고 JUnit 테스트 방법 ...
public void testFoo(){
final AtomicBoolean actionWasCalled = new AtomicBoolean(false);
fooObject.foo(new Runnable(){
public void run(){
actionWasCalled.set(true);
}
});
assertTrue(actionWasCalled.get());
}
System.exit ()를 감싸는 모의 가능한 클래스를 만듭니다.
EricSchaefer에 동의합니다 . 그러나 Mockito 와 같은 훌륭한 조롱 프레임 워크를 사용하면 간단한 구체적인 클래스로 충분하며 인터페이스와 두 가지 구현이 필요하지 않습니다.
System.exit ()에서 테스트 실행 중지
문제:
// do thing1
if(someCondition) {
System.exit(1);
}
// do thing2
System.exit(0)
조롱 Sytem.exit()
은 실행을 종료하지 않습니다. thing2
실행되지 않은 테스트하려는 경우에는 좋지 않습니다.
해결책:
martin 이 제안한 대로이 코드를 리팩터링해야합니다 .
// do thing1
if(someCondition) {
return 1;
}
// do thing2
return 0;
그리고 System.exit(status)
호출 기능을 수행하십시오. 이것은 당신 System.exit()
의 모든 것을 한곳 또는 그 근처에 두게 main()
합니다. 이것은 System.exit()
논리 내부를 깊이 파헤치는 것보다 깨끗 합니다.
암호
싸개:
public class SystemExit {
public void exit(int status) {
System.exit(status);
}
}
본관:
public class Main {
private final SystemExit systemExit;
Main(SystemExit systemExit) {
this.systemExit = systemExit;
}
public static void main(String[] args) {
SystemExit aSystemExit = new SystemExit();
Main main = new Main(aSystemExit);
main.executeAndExit(args);
}
void executeAndExit(String[] args) {
int status = execute(args);
systemExit.exit(status);
}
private int execute(String[] args) {
System.out.println("First argument:");
if (args.length == 0) {
return 1;
}
System.out.println(args[0]);
return 0;
}
}
테스트:
public class MainTest {
private Main main;
private SystemExit systemExit;
@Before
public void setUp() {
systemExit = mock(SystemExit.class);
main = new Main(systemExit);
}
@Test
public void executeCallsSystemExit() {
String[] emptyArgs = {};
// test
main.executeAndExit(emptyArgs);
verify(systemExit).exit(1);
}
}
나는 이미 주어진 답변 중 일부를 좋아하지만 테스트중인 레거시 코드를 얻을 때 종종 유용한 다른 기술을 보여주고 싶었습니다. 주어진 코드 :
public class Foo {
public void bar(int i) {
if (i < 0) {
System.exit(i);
}
}
}
안전한 리팩토링을 수행하여 System.exit 호출을 래핑하는 메소드를 작성할 수 있습니다.
public class Foo {
public void bar(int i) {
if (i < 0) {
exit(i);
}
}
void exit(int i) {
System.exit(i);
}
}
그런 다음 exit를 재정의하는 가짜 테스트를 만들 수 있습니다.
public class TestFoo extends TestCase {
public void testShouldExitWithNegativeNumbers() {
TestFoo foo = new TestFoo();
foo.bar(-1);
assertTrue(foo.exitCalled);
assertEquals(-1, foo.exitValue);
}
private class TestFoo extends Foo {
boolean exitCalled;
int exitValue;
void exit(int i) {
exitCalled = true;
exitValue = i;
}
}
이것은 테스트 사례의 동작을 대체하는 일반적인 기술이며 레거시 코드를 리팩터링 할 때 항상 사용합니다. 일반적으로 내가 떠날 곳이 아니라 테스트중인 기존 코드를 얻는 중간 단계입니다.
api를 간단히 살펴보면 System.exit가 예외 esp를 던질 수 있음을 보여줍니다. 보안 관리자가 vm 종료를 금지하는 경우 해결책은 그러한 관리자를 설치하는 것일 수 있습니다.
You can use the java SecurityManager to prevent the current thread from shutting down the Java VM. The following code should do what you want:
SecurityManager securityManager = new SecurityManager() {
public void checkPermission(Permission permission) {
if ("exitVM".equals(permission.getName())) {
throw new SecurityException("System.exit attempted and blocked.");
}
}
};
System.setSecurityManager(securityManager);
For VonC's answer to run on JUnit 4, I've modified the code as follows
protected static class ExitException extends SecurityException {
private static final long serialVersionUID = -1982617086752946683L;
public final int status;
public ExitException(int status) {
super("There is no escape!");
this.status = status;
}
}
private static class NoExitSecurityManager extends SecurityManager {
@Override
public void checkPermission(Permission perm) {
// allow anything.
}
@Override
public void checkPermission(Permission perm, Object context) {
// allow anything.
}
@Override
public void checkExit(int status) {
super.checkExit(status);
throw new ExitException(status);
}
}
private SecurityManager securityManager;
@Before
public void setUp() {
securityManager = System.getSecurityManager();
System.setSecurityManager(new NoExitSecurityManager());
}
@After
public void tearDown() {
System.setSecurityManager(securityManager);
}
There are environments where the returned exit code is used by the calling program (such as ERRORLEVEL in MS Batch). We have tests around the main methods that do this in our code, and our approach has been to use a similar SecurityManager override as used in other tests here.
Last night I put together a small JAR using Junit @Rule annotations to hide the security manager code, as well as add expectations based on the expected return code. http://code.google.com/p/junitsystemrules/
You can test System.exit(..) with replacing Runtime instance. E.g. with TestNG + Mockito:
public class ConsoleTest {
/** Original runtime. */
private Runtime originalRuntime;
/** Mocked runtime. */
private Runtime spyRuntime;
@BeforeMethod
public void setUp() {
originalRuntime = Runtime.getRuntime();
spyRuntime = spy(originalRuntime);
// Replace original runtime with a spy (via reflection).
Utils.setField(Runtime.class, "currentRuntime", spyRuntime);
}
@AfterMethod
public void tearDown() {
// Recover original runtime.
Utils.setField(Runtime.class, "currentRuntime", originalRuntime);
}
@Test
public void testSystemExit() {
// Or anything you want as an answer.
doNothing().when(spyRuntime).exit(anyInt());
System.exit(1);
verify(spyRuntime).exit(1);
}
}
Calling System.exit() is a bad practice, unless it's done inside a main(). These methods should be throwing an exception which, ultimately, is caught by your main(), who then calls System.exit with the appropriate code.
Use Runtime.exec(String command)
to start JVM in a separate process.
There is a minor problem with the SecurityManager
solution. Some methods, such as JFrame.exitOnClose
, also call SecurityManager.checkExit
. In my application, I didn't want that call to fail, so I used
Class[] stack = getClassContext();
if (stack[1] != JFrame.class && !okToExit) throw new ExitException();
super.checkExit(status);
참고URL : https://stackoverflow.com/questions/309396/java-how-to-test-methods-that-call-system-exit
'Programing' 카테고리의 다른 글
JDBC 및 MySQL로 "통신 링크 실패"해결 (0) | 2020.05.16 |
---|---|
유닉스 / 리눅스 시스템에서 실행중인 프로세스로 커맨드 라인 인수를 얻는 방법? (0) | 2020.05.16 |
두 세트의 교집합을 계산하는 방법은 무엇입니까? (0) | 2020.05.16 |
static_cast <>와 C 스타일 캐스팅의 차이점은 무엇입니까? (0) | 2020.05.16 |
Lodash는 배열에서 중복 제거 (0) | 2020.05.16 |