Java System.exit()処理を無効にして代わりにExceptionを発生させる。

バッチ処理などでSystem.exit()で終わる処理をJUnitでテストするとJVMが終了してしまう。
exit()処理の代わりにExceptionを発生させ、テストを可能にするためのクラス。
基本的にはSystem.exit()など使わずにreturnを使うんだが、古いバッチをメンテナンスするとよくある。

``` SystemExitStub.java
package testlib.system;

import java.security.Permission;

/**
 * System.exit()処理を無効にし代わりにExitExceptionを発生させる.<br>
 * JUnitでSystem.exitのある処理をテストを実施する場合等に使用する。<br>
 * 本クラスはスレッドセーフではないのでマルチスレッド環境では利用不可。<br>
 * シングルトンクラス。
 *
 * @author boss_ape
 */
public final class SystemExitStub extends SecurityManager
{
	/** シングルトンパターンのためのインスタンス. */
	private static final SystemExitStub INSTANCE_STUB = new SystemExitStub();

	/** 現在使われている変更前のセキュリティマネージャ. */
	private static final SecurityManager INSTANCE_SYSTEM = System.getSecurityManager();

	/**
	 * デフォルトコンストラクタの禁止.
	 */
	private SystemExitStub() { }

	/**
	 * このクラスのインスタンスを取得.
	 *
	 * @return このクラスのインスタンス
	 */
	public static SystemExitStub getlnstance()
	{
		return INSTANCE_STUB;
	}

	@Override
	public void checkPermission(Permission permission)
	{
		if ("exitVM".equals(permission.getName()))
			System.out.println("System.exit[exitVM]が呼ばれた");
	}

	@Override
	public void checkExit(int status)
	{
		throw new ExitException(status);
	}

	/**
	 * System.exit()をExitExceptionに変更設定する.
	 */
	public void change()
	{
		// JVMを終了させない
		System.setSecurityManager(INSTANCE_STUB);
	}

	/**
	 * System.exit()を元に戻す.
	 */
	public void reset()
	{
		System.setSecurityManager(INSTANCE_SYSTEM);
	}

	/**
	 * JVMの停止の代わりに発生させるException.
	 *
	 * @author boss_ape
	 */
	public class ExitException extends SecurityException
	{
		public int state = 0;

		public ExitException(int state)
		{
			this.state = state;
		}
	}
}
```

以下、使用サンプル。
Javaバッチとそのテスト。

まずはJavaバッチサンプル

``` SampleBatch.java
package jp.co.ivynetwork.batch;

/**
 * FugaFugaをFugeFugeするバッチ処理.<br>
 * 0:正常終了<br>
 * 1:異常終了
 *
 * @author boss_ape
 */
public class SampleBatch
{
	public static void main(String[] args)
	{
		try {
			// パラメータが何か指定されていたらException発生
			if (null == args)
				throw new NullPointerException();

		} catch (NullPointerException e) {
			System.exit(1);
		}
		System.exit(0);
	}
}
```

上記バッチのJUnitテスト

``` SampleBatchTest.java
package jp.co.ivynetwork.batch;

import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import testlib.system.SystemExitStub;

/**
 * バッチテスト.<br>
 * テスト実行中、System.exit()でJVMが終了しないようにExitExceptionをスローする。
 *
 * @author boss_ape
 */
public class SampleBatchTest
{
	/** System.exit変更管理オブジェクト. */
	private SystemExitStub exit = SystemExitStub.getlnstance();

	/**
	 * テストメソッド実行前にSystem.exit()設定を変更.
	 */
	@Before
	public void changeSystem()
	{
		// System.exit()をExitExceptionに変更
		exit.change();
	}

	/**
	 * テストメソッド実行後にSystem.exit()設定を戻す.
	 */
	@After
	public void resetSystem()
	{
		// System.exit()設定を元に戻す
		exit.reset();
	}

	/**
	 * main処理のテスト.<br>
	 * パラメータnullで実行時、exit(1)で終了する事を確認
	 */
	@Test
	public void testMainパラメータにnullを設定()
	{
		try {
			// バッチ実行
			SampleBatch.main(null);

		// 通常のSystem.exit()が実行された場合、JUnitのランナーそのものが終了してしまう
		} catch (SystemExitStub.ExitException e) {
			// System.exit(1)で終了していたら正常
			assertThat(e.state, is(1));
		}
	}

	/**
	 * main処理のテスト.<br>
	 * パラメータ指定で実行時、exit(0)で終了する事を確認
	 */
	@Test
	public void testMainパラメータに値を設定()
	{
		try {
			// バッチ実行
			SampleBatch.main(new String[] { "パラメータ指定" });

		// 通常のSystem.exit()が実行された場合、JUnitのランナーそのものが終了してしまう
		} catch (SystemExitStub.ExitException e) {
			// System.exit(0)で終了していたら正常
			assertThat(e.state, is(0));
		}
	}
}
```