/* * Copyright (c) 2011 NTT DATA Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package jp.terasoluna.fw.batch.executor; import jp.terasoluna.fw.batch.executor.controller.JobOperator; import jp.terasoluna.fw.util.PropertyUtil; import org.junit.After; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; import org.springframework.beans.BeansException; import org.springframework.beans.factory.BeanCreationException; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextException; import org.springframework.util.ReflectionUtils; import uk.org.lidalia.slf4jtest.TestLogger; import uk.org.lidalia.slf4jtest.TestLoggerFactory; import java.lang.reflect.Field; import java.util.TreeMap; import static java.util.Arrays.asList; import static org.hamcrest.CoreMatchers.instanceOf; import static org.hamcrest.core.Is.is; import static org.junit.Assert.*; import static org.mockito.Mockito.*; import static uk.org.lidalia.slf4jtest.LoggingEvent.error; import static uk.org.lidalia.slf4jtest.LoggingEvent.info; /** * {@code SyncBatchExecutor}のテストケース。 */ public class SyncBatchExecutorTest { final SecurityManager sm = System.getSecurityManager(); private TestLogger logger = TestLoggerFactory .getTestLogger(SyncBatchExecutor.class); private Object originProps; private static Field propsField; /*** * テストケース全体の前処理。<br> * テストケース終了後に復元対象となる{@code PropertyUtil}の内部フィールドである * propsを退避する。 */ @BeforeClass public static void setUpClass() { propsField = ReflectionUtils.findField(PropertyUtil.class, "props"); propsField.setAccessible(true); } /** * テスト前処理。<br> * System.exit()でテストプロセスを止めないセキュリティマネージャを設定する。 * * @throws Exception 予期しない例外 */ @Before public void setUp() throws Exception { System.setSecurityManager(new SecurityManagerEx()); // PropertyUtilの内部プロパティを退避 this.originProps = propsField.get(null); } /** * テスト後処理。<br> * セキュリティマネージャを元に戻す。 * * @throws Exception 予期しない例外 */ @After public void tearDown() throws Exception { logger.clear(); System.setSecurityManager(sm); // PropertyUtilの内部プロパティを復元 propsField.set(null, this.originProps); } /** * mainのテスト 【正常系】 * 事前条件 * ・特になし * 確認項目 * ・doMain()メソッドがコールされ、モックの{@code JobOperator}から *  終了ステータス23でプロセス終了すること。 *  (プロセス終了は{@code ExitException}スローにより代用する。 *  そのため例外キャッチを行っているが正常系としてテストを行う。) * * @throws Exception 予期しない例外 */ @Test public void testMain() throws Exception { try { SyncBatchExecutor.main(new String[0]); fail(); } catch (SecurityManagerEx.ExitException e) { assertEquals(23, e.state); } } /** * doMainのテスト 【正常系】 * 事前条件 * ・特になし * 確認項目 * ・{@code JobOperator#start()}により、ステータスコードが返却されること。 * * @throws Exception 予期しない例外 */ @Test public void testDoMain01() throws Exception { JobOperator mockJobOperator = mock(JobOperator.class); String[] args = new String[] { "aaa", "bbb", "ccc" }; doReturn(456).when(mockJobOperator).start(args); ApplicationContext mockContext = mock(ApplicationContext.class); doReturn(mockJobOperator).when(mockContext) .getBean("syncJobOperator", JobOperator.class); ApplicationContextResolver mockResolver = mock( ApplicationContextResolver.class); doReturn(mockContext).when(mockResolver).resolveApplicationContext(); SyncBatchExecutor target = spy(new SyncBatchExecutor()); doReturn(mockResolver).when(target).findAdminContextResolver(); // テスト実行 assertEquals(456, target.doMain(args)); assertThat(logger.getLoggingEvents(), is(asList( info("[IAL025014] SyncBatchExecutor START."), info("[IAL025015] SyncBatchExecutor END. blogicStatus:456")))); } /** * doMainのテスト 【異常系】 * 事前条件 * ・特になし * 確認項目 * ・{@code resolveApplicationContext()}メソッドによる{@code ApplicationContext}の生成失敗により * {@code BeansException}をスローした場合、 * {@code doMain()}の戻り値が{@code FAIL_TO_OBTAIN_JOB_OPERATOR_CODE}であること。 * * @throws Exception 予期しない例外 */ @Test public void testDoMain02() throws Exception { ApplicationContextResolver mockResolver = mock( ApplicationContextResolver.class); Exception expectThrown = new BeansException("applicationContext creation error.") { private static final long serialVersionUID = 2970034475371975813L; }; doThrow(expectThrown).when(mockResolver).resolveApplicationContext(); SyncBatchExecutor target = spy(new SyncBatchExecutor()); doReturn(mockResolver).when(target).findAdminContextResolver(); // テスト実行 assertEquals(SyncBatchExecutor.FAIL_TO_OBTAIN_JOB_OPERATOR_CODE, target.doMain(new String[] {})); assertThat(logger.getLoggingEvents(), is(asList( info("[IAL025014] SyncBatchExecutor START."), error(expectThrown, "[EAL025060] Failed to processing of JobOperator. please see below the stacktrace."), info("[IAL025015] SyncBatchExecutor END. blogicStatus:255")))); } /** * doMainのテスト 【異常系】 * 事前条件 * ・特になし * 確認項目 * ・{@code ApplicationContext#getBean()}で例外をスローしたとき、 *  コンテキストのクローズ処理が呼び出されること。 * * @throws Exception 予期しない例外 */ @Test public void testDoMain03() throws Exception { ApplicationContextResolver mockResolver = mock( ApplicationContextResolver.class); ApplicationContext mockContext = mock(ApplicationContext.class); Exception expectThrown = new BeanCreationException( "class-load error."); doThrow(expectThrown).when(mockContext).getBean(anyString(), eq(JobOperator.class)); doReturn(mockContext).when(mockResolver).resolveApplicationContext(); SyncBatchExecutor target = spy(new SyncBatchExecutor()); doReturn(mockResolver).when(target).findAdminContextResolver(); // テスト実行 assertEquals(SyncBatchExecutor.FAIL_TO_OBTAIN_JOB_OPERATOR_CODE, target.doMain(new String[] {})); assertThat(logger.getLoggingEvents(), is(asList( info("[IAL025014] SyncBatchExecutor START."), error(expectThrown, "[EAL025060] Failed to processing of JobOperator. please see below the stacktrace."), info("[IAL025015] SyncBatchExecutor END. blogicStatus:255")))); verify(mockResolver).closeApplicationContext(any(ApplicationContext.class)); } /** * doMainのテスト 【異常系】 * 事前条件 * ・特になし * 確認項目 * ・{@code ApplicationContext#getBean()}で例外をスローし、コンテキストの *  クローズ時にも例外をスローした場合、エラーログが出力されること。 * * @throws Exception 予期しない例外 */ @Test public void testDoMain04() throws Exception { ApplicationContextResolver mockResolver = mock( ApplicationContextResolver.class); ApplicationContext mockContext = mock(ApplicationContext.class); Exception expectThrown = new BeanCreationException( "class-load error."); Exception closeThrown = new ApplicationContextException("close error."); doThrow(expectThrown).when(mockContext).getBean(anyString(), eq(JobOperator.class)); doReturn(mockContext).when(mockResolver).resolveApplicationContext(); doThrow(closeThrown).when(mockResolver).closeApplicationContext(mockContext); SyncBatchExecutor target = spy(new SyncBatchExecutor()); doReturn(mockResolver).when(target).findAdminContextResolver(); // テスト実行 assertEquals(SyncBatchExecutor.FAIL_TO_OBTAIN_JOB_OPERATOR_CODE, target.doMain(new String[] {})); assertThat(logger.getLoggingEvents(), is(asList( info("[IAL025014] SyncBatchExecutor START."), error(expectThrown, "[EAL025060] Failed to processing of JobOperator. please see below the stacktrace."), info("[IAL025015] SyncBatchExecutor END. blogicStatus:255"), error(closeThrown, "[EAL025062] Failed to close the ApplicationContext.")))); } /** * findAdminContextResolverのテスト 【正常系】 * 事前条件 * ・特になし * 確認項目 *  {@code ApplicationContextResolverImpl}インスタンスが返却されること。 * * @throws Exception 予期しない例外 */ @Test public void testFindAdminContextResolver01() throws Exception { propsField.set(null, new TreeMap<String, String>()); SyncBatchExecutor target = new SyncBatchExecutor(); // テスト実行 ApplicationContextResolver resolver = target .findAdminContextResolver(); assertThat(resolver, is(instanceOf(ApplicationContextResolverImpl.class))); } }