2016-08-25 40 views
5

コマンドラインからの引数を期待している非常に基本的なSpringブートアプリケーションがあり、動作しません。ここにコードがあります。junit testを使用してSpringブートアプリケーションにコマンドライン引数を渡す

@SpringBootApplication 
public class Application implements CommandLineRunner { 

    private static final Logger log = LoggerFactory.getLogger(Application.class); 

    @Autowired 
    private Reader reader; 

    @Autowired 
    private Writer writer; 

    public static void main(String[] args) { 
     SpringApplication.run(Application.class, args); 
    } 

    @Override 
    public void run(String... args) throws Exception { 

     Assert.notEmpty(args); 

     List<> cities = reader.get("Berlin"); 
     writer.write(cities); 
    } 
} 

ここは私のJUnitテストクラスです。

@RunWith(SpringRunner.class) 
@SpringBootTest 
public class CityApplicationTests { 

    @Test 
    public void contextLoads() { 
    } 
} 

ここで、Assert.notEmpty()は、引数を渡す必要があります。しかし、今、私はJUnitテストを書いています。しかし、私はAssertから例外を上げることになる。

2016-08-25 16:59:38.714 ERROR 9734 --- [   main] o.s.boot.SpringApplication    : Application startup failed 

java.lang.IllegalStateException: Failed to execute CommandLineRunner 
    at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:801) ~[spring-boot-1.4.0.RELEASE.jar:1.4.0.RELEASE] 
    at org.springframework.boot.SpringApplication.callRunners(SpringApplication.java:782) ~[spring-boot-1.4.0.RELEASE.jar:1.4.0.RELEASE] 
    at org.springframework.boot.SpringApplication.afterRefresh(SpringApplication.java:769) ~[spring-boot-1.4.0.RELEASE.jar:1.4.0.RELEASE] 
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:314) ~[spring-boot-1.4.0.RELEASE.jar:1.4.0.RELEASE] 
    at org.springframework.boot.test.context.SpringBootContextLoader.loadContext(SpringBootContextLoader.java:111) [spring-boot-test-1.4.0.RELEASE.jar:1.4.0.RELEASE] 
    at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContextInternal(DefaultCacheAwareContextLoaderDelegate.java:98) [spring-test-4.3.2.RELEASE.jar:4.3.2.RELEASE] 
    at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:116) [spring-test-4.3.2.RELEASE.jar:4.3.2.RELEASE] 
    at org.springframework.test.context.support.DefaultTestContext.getApplicationContext(DefaultTestContext.java:83) [spring-test-4.3.2.RELEASE.jar:4.3.2.RELEASE] 
    at org.springframework.test.context.support.DependencyInjectionTestExecutionListener.injectDependencies(DependencyInjectionTestExecutionListener.java:117) [spring-test-4.3.2.RELEASE.jar:4.3.2.RELEASE] 
    at org.springframework.test.context.support.DependencyInjectionTestExecutionListener.prepareTestInstance(DependencyInjectionTestExecutionListener.java:83) [spring-test-4.3.2.RELEASE.jar:4.3.2.RELEASE] 
    at org.springframework.boot.test.autoconfigure.AutoConfigureReportTestExecutionListener.prepareTestInstance(AutoConfigureReportTestExecutionListener.java:46) [spring-boot-test-autoconfigure-1.4.0.RELEASE.jar:1.4.0.RELEASE] 
    at org.springframework.test.context.TestContextManager.prepareTestInstance(TestContextManager.java:230) [spring-test-4.3.2.RELEASE.jar:4.3.2.RELEASE] 
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.createTest(SpringJUnit4ClassRunner.java:228) [spring-test-4.3.2.RELEASE.jar:4.3.2.RELEASE] 
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner$1.runReflectiveCall(SpringJUnit4ClassRunner.java:287) [spring-test-4.3.2.RELEASE.jar:4.3.2.RELEASE] 
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12) [junit-4.12.jar:4.12] 
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.methodBlock(SpringJUnit4ClassRunner.java:289) [spring-test-4.3.2.RELEASE.jar:4.3.2.RELEASE] 
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:247) [spring-test-4.3.2.RELEASE.jar:4.3.2.RELEASE] 
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:94) [spring-test-4.3.2.RELEASE.jar:4.3.2.RELEASE] 
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290) [junit-4.12.jar:4.12] 
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71) [junit-4.12.jar:4.12] 
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288) [junit-4.12.jar:4.12] 
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58) [junit-4.12.jar:4.12] 
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268) [junit-4.12.jar:4.12] 
    at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61) [spring-test-4.3.2.RELEASE.jar:4.3.2.RELEASE] 
    at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70) [spring-test-4.3.2.RELEASE.jar:4.3.2.RELEASE] 
    at org.junit.runners.ParentRunner.run(ParentRunner.java:363) [junit-4.12.jar:4.12] 
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:191) [spring-test-4.3.2.RELEASE.jar:4.3.2.RELEASE] 
    at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:86) [.cp/:na] 
    at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38) [.cp/:na] 
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:459) [.cp/:na] 
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:678) [.cp/:na] 
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:382) [.cp/:na] 
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:192) [.cp/:na] 
Caused by: java.lang.IllegalArgumentException: [Assertion failed] - this array must not be empty: it must contain at least 1 element 
    at org.springframework.util.Assert.notEmpty(Assert.java:222) ~[spring-core-4.3.2.RELEASE.jar:4.3.2.RELEASE] 
    at org.springframework.util.Assert.notEmpty(Assert.java:234) ~[spring-core-4.3.2.RELEASE.jar:4.3.2.RELEASE] 
    at com.deepakshakya.dev.Application.run(Application.java:33) ~[classes/:na] 
    at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:798) ~[spring-boot-1.4.0.RELEASE.jar:1.4.0.RELEASE] 
    ... 32 common frames omitted 

どのようにパラメータを渡すのですか?

+2

正直言って、それもテストしたいですか?私は追加のプロファイルを作成し、そのプロファイルが使用されるときにBeanを除外し、統合テストでそのプロファイルを使用して完了します。 'run()'メソッドをテストするには、何をする必要があるのか​​、常にユニットテストを書くことができます。そのためにアプリケーションを起動する必要はありません。 – g00glen00b

+0

また、テストをどのように開始しているのかわからなかったので、どのように引数を渡そうとしているのか分かりません。 – chrylis

+0

私はJUnitクラスをアプリケーションの呼び出し元から更新しました。 – divinedragon

答えて

2

私はあなたのソリューションが(Spring用に独自のテストフレームワークを実装するまで)あなたが提示した方法では動作しないことを誇りに思っています。

これは、テストを実行しているときに、Spring(より具体的には、テストSpringBootContextLoader)が独自の方法でアプリケーションを実行するためです。 SpringApplicationをインスタンス化し、引数なしでrunメソッドを呼び出します。また、アプリケーションで実装されたmainメソッドを使用することもありません。

ただし、テストすることができるようにアプリケーションをリファクタリングすることができます。

私は(あなたがSpringを使用しているので)最も簡単な解決策は、純粋なコマンドライン引数の代わりにスプリング構成プロパティを使用して実装できると思います。

@SpringBootApplication 
public class Application implements CommandLineRunner { 

    @Value("${myCustomArgs.customArg1}") 
    private String customArg1; 

    public static void main(String[] args) { 
     SpringApplication.run(Application.class, args); 
    } 

    @Override 
    public void run(String... args) throws Exception { 

     Assert.notNull(customArg1); 
     //... 
    } 
} 

サンプルテスト:

@Value注釈を使用して

読書パラメータ(つまり、バネの主な目的configuration properties仕組みですので、しかし、あなたは、このソリューションは、「コンフィギュレーションの引数」のためではなく使用することを認識する必要があります)

@RunWith(SpringRunner.class) 
@SpringBootTest({"myCustomArgs.customArg1=testValue"}) 
public class CityApplicationTests { 

    @Test 
    public void contextLoads() { 
    } 
} 

そして、あなたのコマンドラインアプリケーションを実行するときだけ追加してカスタムのparams:

--myCustomArgs.customArg1=testValue

3

私はSpringBootを式の外に置きます。

スプリングブートをテストせずに、runメソッドをテストするだけで、Springブートを実行する必要はありません。 私は、このテストの目的は回帰のためのもので、argsが与えられていないときにアプリケーションが常にIllegalArgumentExceptionを投げることを保証していると思いますか?

@RunWith(MockitoJUnitRunner.class) 
public class ApplicationTest { 

    @InjectMocks 
    private Application app = new Application(); 

    @Mock 
    private Reader reader; 

    @Mock 
    private Writer writer; 

    @Test(expected = IllegalArgumentException.class) 
    public void testNoArgs() throws Exception { 
     app.run(); 
    } 

    @Test 
    public void testWithArgs() throws Exception { 
     List list = new ArrayList(); 
     list.add("test"); 
     Mockito.when(reader.get(Mockito.anyString())).thenReturn(list); 

     app.run("myarg"); 

     Mockito.verify(reader, VerificationModeFactory.times(1)).get(Mockito.anyString()); 
     Mockito.verify(writer, VerificationModeFactory.times(1)).write(list); 
    } 
} 

私はリーダーのためのモックを注入するMockitoを使用し、脚本::良い古いユニットテストはまだ独身の方法をテストするために働く

<dependency> 
    <groupId>org.mockito</groupId> 
    <artifactId>mockito-all</artifactId> 
    <version>1.9.0</version> 
    <scope>test</scope> 
</dependency> 
+0

あなたの意見に同意してください。しかし、私は機能テストの観点から見て、コマンドラインから異なる値を提供することによってコードの実行を具体的にテストしたかったのです。それをする方法がないように思えます。 – divinedragon

4

I'veは、JUnitテストを作成する方法を見つけるために管理しています私のテストでApplicationContextを注入し、必要なパラメータでCommandLineRunnerを呼び出すことで、SpringBootでうまく動作しました。

最終的なコードは以下のようになります。

package my.package. 

import org.junit.Test; 
import org.junit.runner.RunWith; 
import org.springframework.beans.factory.annotation.Autowired; 
import org.springframework.boot.CommandLineRunner; 
import org.springframework.boot.test.context.SpringBootTest; 
import org.springframework.context.ApplicationContext; 
import org.springframework.test.context.junit4.SpringRunner; 

@RunWith(SpringRunner) 
@SpringBootTest 
class AsgardBpmClientApplicationIT { 

    @Autowired 
    ApplicationContext ctx; 

    @Test 
    void testRun() { 
     CommandLineRunner runner = ctx.getBean(CommandLineRunner.class); 
     runner.run ("-k", "arg1", "-i", "arg2"); 
    } 

} 
+0

これはすばらしい解決策です。しかし、テストは公開する必要があります。 –

0

としてはin this answerは、春ブーツは現在、それが使用するDefaultApplicationArgumentsを置き換える/傍受する方法を提供していません述べました。私がこれを解決するために使用した自然なブート方法は、ランナーロジックを強化し、いくつかのautowiredプロパティを使用することでした。

まず、私は、プロパティ・コンポーネントを作成しました:

@ConfigurationProperties("app") @Component @Data 
public class AppProperties { 
    boolean failOnEmptyFileList = true; 
    boolean exitWhenFinished = true; 
} 

...私のランナーへのプロパティのコンポーネントをautowired:

@Service 
public class Loader implements ApplicationRunner { 

    private AppProperties properties; 

    @Autowired 
    public Loader(AppProperties properties) { 
     this.properties = properties; 
    } 
    ... 

を...と、runで私だけassert'edそのプロパティが有効になっている場合、通常のアプリケーション使用の場合、デフォルトでtrueになります。

@Override 
public void run(ApplicationArguments args) throws Exception { 
    if (properties.isFailOnEmptyFileList()) { 
     Assert.notEmpty(args.getNonOptionArgs(), "Pass at least one filename on the command line"); 
    } 

    // ...do some loading of files and such 

    if (properties.isExitWhenFinished()) { 
     System.exit(0); 
    } 
} 
それと

は、私はユニットテストに優しい方法で実行するために、これらのプロパティを微調整することができます。

@RunWith(SpringRunner.class) 
@SpringBootTest(properties = { 
     "app.failOnEmptyFileList=false", 
     "app.exitWhenFinished=false" 
}) 
public class InconsistentJsonApplicationTests { 

    @Test 
    public void contextLoads() { 
    } 

} 

私の特定のランナーは通常System.exit(0)を呼び出し、そのように出て行くことは半でユニットテストを離れたので、私はexitWhenFinished一部を必要に応じて失敗した状態。

関連する問題