/* * Copyright (c) 2016 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 static org.hamcrest.core.Is.is; import static org.hamcrest.core.StringEndsWith.endsWith; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertSame; import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import static org.mockito.Mockito.mock; import java.lang.reflect.Field; import java.util.TreeMap; import org.junit.After; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; import org.springframework.beans.BeansException; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.expression.spel.SpelEvaluationException; import org.springframework.util.ReflectionUtils; import jp.terasoluna.fw.batch.executor.vo.BatchJobData; import jp.terasoluna.fw.batch.message.MessageAccessor; import jp.terasoluna.fw.util.PropertyUtil; /** * {@code ApplicationContextResolverImpl}のテストケース。 */ public class ApplicationContextResolverImplTest { private Object originProps; private static Field propsField; private ApplicationContextResolverImpl target; private ApplicationContext parent; /*** * テストケース全体の前処理。<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 { // PropertyUtilの内部プロパティを退避 this.originProps = propsField.get(null); this.target = new ApplicationContextResolverImpl(); this.parent = new ClassPathXmlApplicationContext("beansDef/commonContext.xml"); } /** * テスト後処理。<br> * セキュリティマネージャを元に戻す。 * * @throws Exception 予期しない例外 */ @After public void tearDown() throws Exception { // PropertyUtilの内部プロパティを復元 propsField.set(null, this.originProps); } /** * resolveAdminContextのテスト 【正常系】 * 事前条件 * ・特になし * 確認項目 * ・AdminContext.xml,AdminDataSource.xmlによる *  {@code ApplicationContext}が生成されること。 * * @throws Exception 予期しない例外 */ @Test public void testResolveAdminContext01() throws Exception { ApplicationContextResolver resolver = new ApplicationContextResolverImpl(); // テスト実行 ApplicationContext context = resolver.resolveApplicationContext(); assertTrue(context.containsBean("syncJobOperator")); assertTrue(context.containsBean("systemDao")); assertNull(context.getParent()); } /** * resolveAdminContextのテスト 【異常系】 * 事前条件 * ・特になし * 確認項目 * ・プロパティにBean定義ファイルのクラスパスが存在しないとき、 *  BeansExceptionがスローされること。 * * @throws Exception 予期しない例外 */ @Test public void testResolveAdminContext02() throws Exception { TreeMap<String, String> props = new TreeMap<>(); propsField.set(null, props); ApplicationContextResolver resolver = new ApplicationContextResolverImpl(); // テスト実行 try { resolver.resolveApplicationContext(); } catch (BeansException e) { assertThat(e.getMessage(), is(endsWith( "[EAL025003] Bean definition default file name is not set. please confirm batch.properties."))); } } /** * resolveApplicationContext(BatchJobData)のテスト 【正常系】 * <pre> * 事前条件 * ・なし * 確認項目 * ・業務DIコンテナである{@code ApplicationContext}が返却されること。 * </pre> * * @throws Exception 予期しない例外 */ @Test public void testResolveApplicationContextBatchJobData01() throws Exception { BatchJobData batchJobData = new BatchJobData(); batchJobData.setJobAppCd("B000001"); target.parent = this.parent; target.classpath = "beansDef/"; // テスト実行 ApplicationContext ctx = target.resolveApplicationContext(batchJobData); // 業務DIコンテナのBeanが取得できること。 B000001BLogic blogic = ctx .getBean("B000001BLogic", B000001BLogic.class); assertNotNull(blogic); // 親のDIコンテナのBeanが取得できること。 MessageAccessor msg = ctx.getBean("msgAcc", MessageAccessor.class); assertNotNull(msg); } /** * resolveApplicationContext(BatchJobData)のテスト 【異常系】 * <pre> * 事前条件 * ・なし * 確認項目 * ・Bean定義ファイルが生成できないジョブ業務コードを指定したとき、 *  {@code BeansException}をスローすること。 * </pre> * * @throws Exception 予期しない例外 */ @Test public void testResolveApplicationContextBatchJobData02() throws Exception { BatchJobData batchJobData = new BatchJobData(); batchJobData.setJobAppCd("not-defined"); target.parent = this.parent; target.classpath = "beansDef/"; // テスト実行 try { target.resolveApplicationContext(batchJobData); fail(); } catch (BeansException e) { assertTrue(e.getMessage().contains( "IOException parsing XML document from class path resource [beansDef/not-defined.xml]")); } } /** * resolveApplicationContext(BatchJobData)のテスト 【正常系】 * <pre> * 事前条件 * ・なし * 確認項目 *  業務DIコンテナである{@code ApplicationContext}の親コンテナが *  setParent()により指定されたものと同一になること。 * </pre> * * @throws Exception 予期しない例外 */ @Test public void testResolveApplicationContextBatchJobData03() throws Exception { BatchJobData batchJobData = new BatchJobData(); batchJobData.setJobAppCd("B000001"); target.parent = this.parent; target.classpath = "beansDef/"; // テスト実行 ApplicationContext ctx = target.resolveApplicationContext(batchJobData); // 業務用DIコンテナの親コンテナが、業務共通DIコンテナと同一であること assertSame(parent, ctx.getParent()); } /** * resolveApplicationContext(BatchJobData)のテスト 【正常系】 * <pre> * 事前条件 * ・なし * 確認項目 *  親コンテナを指定せず業務コンテキストの生成を行った場合、 * 業務コンテキストに親コンテキストが存在しないこと。 * </pre> * * @throws Exception 予期しない例外 */ @Test public void testResolveApplicationContextBatchJobData04() throws Exception { BatchJobData batchJobData = new BatchJobData(); batchJobData.setJobAppCd("B000001"); target.classpath = "beansDef/"; // テスト実行 ApplicationContext ctx = target.resolveApplicationContext(batchJobData); // 業務用DIコンテナの親コンテナが、業務共通DIコンテナと同一であること assertNull(ctx.getParent()); } /** * getBeanFileName()のテスト 【正常系】 * <pre> * 事前条件 * ・なし * 確認項目 * ・classpathプロパティがnullのときディレクトリ指定がなく、 *  ファイル名のみ{@code jobAppCd + ".xml"}というファイルパスが取得できること。 * </pre> * * @throws Exception 予期しない例外 */ @Test public void testGetBeanFileName01() throws Exception { BatchJobData batchJobData = new BatchJobData() {{ setJobAppCd("jobAppCd"); }}; target.classpath = null; // テスト実行 assertEquals("jobAppCd.xml", target.getBeanFileName(batchJobData)); } /** * getBeanFileName()のテスト 【正常系】 * <pre> * 事前条件 * ・なし * 確認項目 * ・classpathプロパティが空文字のときディレクトリ指定がなく、 *  ファイル名のみ{@code jobAppCd + ".xml"}というファイルパスが取得できること。 * </pre> * * @throws Exception 予期しない例外 */ @Test public void testGetBeanFileName02() throws Exception { BatchJobData batchJobData = new BatchJobData() {{ setJobAppCd("jobAppCd"); }}; target.classpath = ""; // テスト実行 assertEquals("jobAppCd.xml", target.getBeanFileName(batchJobData)); } /** * getBeanFileName()のテスト 【正常系】 * <pre> * 事前条件 * ・なし * 確認項目 * ・classpathプロパティが空文字のときディレクトリ指定がなく、 *  ジョブ業務コードがnullのとき、".xml"というファイルパスが取得できること。 * </pre> * * @throws Exception 予期しない例外 */ @Test public void testGetBeanFileName03() throws Exception { BatchJobData batchJobData = new BatchJobData() {{ setJobAppCd(null); }}; target.classpath = ""; // テスト実行 assertEquals(".xml", target.getBeanFileName(batchJobData)); } /** * getBeanFileName()のテスト 【正常系】 * <pre> * 事前条件 * ・なし * 確認項目 * ・classpathプロパティが"classpath/"のとき、 *  "{@code classpath/jobAppCd.xml}というファイルパスが取得できること。 * </pre> * * @throws Exception 予期しない例外 */ @Test public void testGetBeanFileName04() throws Exception { BatchJobData batchJobData = new BatchJobData() {{ setJobAppCd("jobAppCd"); }}; target.classpath = "classpath/"; // テスト実行 assertEquals("classpath/jobAppCd.xml", target.getBeanFileName(batchJobData)); } /** * getBeanFileName()のテスト 【正常系】 * <pre> * 事前条件 * ・なし * 確認項目 * ・classpathプロパティが"classpath/${jobAppCd}/"、ジョブ業務コードが"B000001"のとき、 *  "{@code classpath/B0000001/B0000001.xml}というファイルパスが取得できること。 * </pre> * * @throws Exception 予期しない例外 */ @Test public void testGetBeanFileName05() throws Exception { BatchJobData batchJobData = new BatchJobData() {{ setJobAppCd("B0000001"); }}; target.classpath = "classpath/${jobAppCd}/"; // テスト実行 assertEquals("classpath/B0000001/B0000001.xml", target.getBeanFileName(batchJobData)); } /** * getBeanFileName()のテスト 【異常系】 * <pre> * 事前条件 * ・なし * 確認項目 * ・EL式で存在しないプロパティ名を明示した場合に{@code SpelEvaluationException}がスローされること。 * </pre> * * @throws Exception 予期しない例外 */ @Test public void testGetBeanFileName06() throws Exception { BatchJobData batchJobData = new BatchJobData() {{ setJobAppCd("B0000001"); }}; target.classpath = "classpath/${noDefProperty}/"; // テスト実行 try { target.getBeanFileName(batchJobData); fail(); } catch (SpelEvaluationException e) { assertTrue(e.getMessage().contains( "EL1008E: Property or field 'noDefProperty' cannot be found on object of type 'jp.terasoluna.fw.batch.executor.ApplicationContextResolverImplTest")); } } /** * getBeanFileName()のテスト 【異常系】 * <pre> * 事前条件 * ・なし * 確認項目 * ・EL式として不正なプロパティ名を明示した場合に{@code ParseException}がスローされること。 * </pre> * * @throws Exception 予期しない例外 */ @Test public void testGetBeanFileName07() throws Exception { BatchJobData batchJobData = new BatchJobData() {{ setJobAppCd("B0000001"); }}; target.classpath = "classpath/${jobAppCd/"; // テスト実行 try { target.getBeanFileName(batchJobData); fail(); } catch (IllegalArgumentException e) { assertEquals( "[EAL025058] Invalid format in batch.properties. key:beanDefinition.business.classpath", e.getMessage()); } } /** * getBeanFileName()のテスト 【異常系】 * <pre> * 事前条件 * ・なし * 確認項目 * ・引数の{@code BatchJobData}が{@code null}であるとき、 *  {@code IllegalArgumentException}がスローされること。 * </pre> * * @throws Exception 予期しない例外 */ @Test public void testGetBeanFileName08() throws Exception { target.classpath = "classpath/"; // テスト実行 try { target.getBeanFileName(null); fail(); } catch (IllegalArgumentException e) { assertEquals( "[Assertion failed] - this argument is required; it must not be null", e.getMessage()); } } /** * getBeanFileName()のテスト 【正常系】 * <pre> * 事前条件 * ・なし * 確認項目 * ・引数に${jobAppCdUpper}が含まれているとき、プロパティが大文字化された *  ディレクトリパスが取得できること。 * </pre> * * @throws Exception 予期しない例外 */ @Test public void testGetBeanFileName09() throws Exception { BatchJobData batchJobData = new BatchJobData() {{ setJobAppCd("to_upper"); }}; target.classpath = "classpath/${jobAppCdUpper}/"; // テスト実行 assertEquals("classpath/TO_UPPER/to_upper.xml", target.getBeanFileName(batchJobData)); } /** * getBeanFileName()のテスト 【正常系】 * <pre> * 事前条件 * ・なし * 確認項目 * ・引数に${jobAppCdLower}が含まれているとき、プロパティが小文字化された *  ディレクトリパスが取得できること。 * </pre> * * @throws Exception 予期しない例外 */ @Test public void testGetBeanFileName10() throws Exception { BatchJobData batchJobData = new BatchJobData() {{ setJobAppCd("TO_LOWER"); }}; target.classpath = "classpath/${jobAppCdLower}/"; // テスト実行 assertEquals("classpath/to_lower/TO_LOWER.xml", target.getBeanFileName(batchJobData)); } /** * closeApplicationContext()のテスト 【正常系】 * <pre> * 事前条件 * ・なし * 確認項目 * ・子のDIコンテナがクローズされ、親DIコンテナからはBeanが取得できること。 * </pre> * * @throws Exception 予期しない例外 */ @Test public void testCloseApplicationContext01() throws Exception { ApplicationContext child = new ClassPathXmlApplicationContext( new String[] { "beansDef/B000001.xml" }, parent); // テスト実行 target.closeApplicationContext(child); // 子のDIコンテナにアクセスした場合、IllegalStateExceptionが発生すること。 try { child.containsBean("B000001BLogic"); fail(); } catch (IllegalStateException e) { } // 親DIコンテナのBeanは取得可能であること。 assertTrue(parent.containsBean("msgAcc")); } /** * closeApplicationContext()のテスト 【正常系】 * <pre> * 事前条件 * ・なし * 確認項目 * ・{@code AbstractApplicationContext}ではないコンテキストを指定した場合 *  例外が発生しないこと。 * </pre> * * @throws Exception 予期しない例外 */ @Test public void testCloseApplicationContext02() throws Exception { ApplicationContext child = mock(ApplicationContext.class); // テスト実行 target.closeApplicationContext(child); } /** * afterPropertiesSet()のテスト 【正常系】 * <pre> * 事前条件 * ・なし * 確認項目 * ・commonContextClassPathがnullのとき、親コンテキストが生成されないこと。 * </pre> * * @throws Exception */ @Test public void testAfterPropertiesSet01() throws Exception { ApplicationContextResolverImpl target = new ApplicationContextResolverImpl(); // テスト実行 target.afterPropertiesSet(); assertNull(target.parent); } /** * afterPropertiesSet()のテスト 【正常系】 * <pre> * 事前条件 * ・なし * 確認項目 * ・commonContextClassPathが空の文字列配列のとき、親コンテキストが生成されないこと。 * </pre> * * @throws Exception */ @Test public void testAfterPropertiesSet02() throws Exception { ApplicationContextResolverImpl target = new ApplicationContextResolverImpl(); target.setCommonContextClassPath(new String[0]); // テスト実行 target.afterPropertiesSet(); assertNull(target.parent); } /** * afterPropertiesSet()のテスト 【正常系】 * <pre> * 事前条件 * ・なし * 確認項目 * ・commonContextClassPathにコンテキストのクラスパスが指定されているとき、親コンテキストが生成されること。 * </pre> * * @throws Exception */ @Test public void testAfterPropertiesSet03() throws Exception { ApplicationContextResolverImpl target = new ApplicationContextResolverImpl(); target.setCommonContextClassPath(new String[]{"beansDef/commonContext.xml", "beansDef/dataSource.xml"}); // テスト実行 target.afterPropertiesSet(); assertNotNull(target.parent); assertNotNull(target.parent.getBean("defaultExceptionHandler")); } }