2016-08-30 14 views
1

私はSpring Bootの自動構成が動作することを確認するためにかなりのテストを書くことに決めました。必要なすべてのBeanが依存関係とともに作成されます。カスタムSpringブート自動構成が有効かどうかのテスト

package org.project.module.autoconfigure; 

import org.project.module.SomeFactory; 
import org.project.module.SomeProducer; 
import org.project.module.SomeServiceClient; 
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; 
import org.springframework.context.annotation.Bean; 
import org.springframework.context.annotation.ComponentScan; 
import org.springframework.context.annotation.Configuration; 

/** 
* Spring Boot simple auto-configuration. 
* 
* @author istepanov 
*/ 
@Configuration 
@ComponentScan("org.project.module.support") 
public class SomeAutoConfiguration { 

    @Bean 
    @ConditionalOnMissingBean 
    public SomeFactory someFactory() { 
     return new SomeFactory(); 
    } 

    @Bean 
    @ConditionalOnMissingBean 
    public SomeServiceClient someServiceClient() { 
     return new SomeServiceClient(); 
    } 

    @Bean 
    @ConditionalOnMissingBean 
    public SomeProducer someProducer() { 
     return new SomeProducer(); 
    } 
} 

とテストは次のとおりです:

自動設定がある

package org.project.module.autoconfigure; 

import org.project.module.SomeFactory; 
import org.project.module.SomeProducer; 
import org.project.module.SomeServiceClient; 
import org.junit.Test; 
import org.junit.runner.RunWith; 
import org.springframework.beans.factory.annotation.Autowired; 
import org.springframework.boot.test.context.SpringBootTest; 
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; 

import static org.assertj.core.api.Assertions.assertThat; 

/** 
* Tests for {@code SomeAutoConfiguration}. 
* 
* @author istepanov 
*/ 
@RunWith(SpringJUnit4ClassRunner.class) 
@SpringBootTest(classes = {SomeAutoConfiguration.class}, webEnvironment = SpringBootTest.WebEnvironment.NONE) 
public class SomeAutoConfigurationTest { 

    @Autowired 
    private SomeFactory someFactory; 
    @Autowired 
    private SomeServiceClient someServiceClient; 
    @Autowired 
    private SomeProducer someProducer; 

    @Test 
    public void someFactory_isNotNull() { 
     assertThat(someFactory).isNotNull(); 
    } 

    @Test 
    public void someServiceClient_isNotNull() { 
     assertThat(someServiceClient).isNotNull(); 
    } 

    @Test 
    public void someProducer_isNotNull() { 
     assertThat(someProducer).isNotNull(); 
    } 
} 

しかし、実際にテストが例外で失敗 - 依存豆、@ComponentScanがロードされることが予想され、実際に欠けている:

java.lang.IllegalStateException: Failed to load ApplicationContext 
    at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:124) 
    at org.springframework.test.context.support.DefaultTestContext.getApplicationContext(DefaultTestContext.java:83) 
    at org.springframework.boot.test.autoconfigure.AutoConfigureReportTestExecutionListener.prepareTestInstance(AutoConfigureReportTestExecutionListener.java:49) 
    at org.springframework.test.context.TestContextManager.prepareTestInstance(TestContextManager.java:230) 
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.createTest(SpringJUnit4ClassRunner.java:228) 
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner$1.runReflectiveCall(SpringJUnit4ClassRunner.java:287) 
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12) 
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.methodBlock(SpringJUnit4ClassRunner.java:289) 
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:247) 
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:94) 
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290) 
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71) 
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288) 
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58) 
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268) 
    at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61) 
    at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70) 
    at org.junit.runners.ParentRunner.run(ParentRunner.java:363) 
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:191) 
    at org.junit.runner.JUnitCore.run(JUnitCore.java:137) 
    at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:68) 
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'someFacade': Unsatisfied dependency expressed through method 'setSomeMetrics' parameter 0: Error creating bean with name 'someMetrics': Unsatisfied dependency expressed through method 'setCounterService' parameter 0: No qualifying bean of type [org.springframework.boot.actuate.metrics.CounterService] found for dependency [org.springframework.boot.actuate.metrics.CounterService]: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {}; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [org.springframework.boot.actuate.metrics.CounterService] found for dependency [org.springframework.boot.actuate.metrics.CounterService]: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {}; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'someMetrics': Unsatisfied dependency expressed through method 'setCounterService' parameter 0: No qualifying bean of type [org.springframework.boot.actuate.metrics.CounterService] found for dependency [org.springframework.boot.actuate.metrics.CounterService]: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {}; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [org.springframework.boot.actuate.metrics.CounterService] found for dependency [org.springframework.boot.actuate.metrics.CounterService]: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {} 
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredMethodElement.inject(AutowiredAnnotationBeanPostProcessor.java:648) 
    at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:88) 
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:349) 
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1214) 
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:543) 
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:482) 
    at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:306) 
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230) 
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:302) 
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197) 
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:776) 
    at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:861) 
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:541) 
    at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:759) 
    at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:369) 
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:313) 
    at org.springframework.boot.test.context.SpringBootContextLoader.loadContext(SpringBootContextLoader.java:111) 
    at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContextInternal(DefaultCacheAwareContextLoaderDelegate.java:98) 
    at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:116) 
    ... 22 more 
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'someMetrics': Unsatisfied dependency expressed through method 'setCounterService' parameter 0: No qualifying bean of type [org.springframework.boot.actuate.metrics.CounterService] found for dependency [org.springframework.boot.actuate.metrics.CounterService]: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {}; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [org.springframework.boot.actuate.metrics.CounterService] found for dependency [org.springframework.boot.actuate.metrics.CounterService]: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {} 
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredMethodElement.inject(AutowiredAnnotationBeanPostProcessor.java:648) 
    at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:88) 
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:349) 
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1214) 
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:543) 
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:482) 
    at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:306) 
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230) 
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:302) 
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202) 
    at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:207) 
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.findAutowireCandidates(DefaultListableBeanFactory.java:1214) 
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1054) 
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1019) 
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredMethodElement.inject(AutowiredAnnotationBeanPostProcessor.java:640) 
    ... 40 more 
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [org.springframework.boot.actuate.metrics.CounterService] found for dependency [org.springframework.boot.actuate.metrics.CounterService]: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {} 
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.raiseNoMatchingBeanFound(DefaultListableBeanFactory.java:1406) 
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1057) 
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1019) 
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredMethodElement.inject(AutowiredAnnotationBeanPostProcessor.java:640) 
    ... 54 more 

私が逃したアイデアは何ですか?

P.S:また行方不明SomeMetricsを追加:

package org.project.module.support.metrics; 

import org.project.module.support.SomeProperties; 
import org.springframework.beans.factory.annotation.Autowired; 
import org.springframework.beans.factory.annotation.Value; 
import org.springframework.boot.actuate.metrics.CounterService; 
import org.springframework.boot.actuate.metrics.GaugeService; 
import org.springframework.stereotype.Component; 

import javax.annotation.PostConstruct; 

import static org.mockito.Mockito.mock; 

/** 
* Customization for Spring Actuator, defines application-specific counters and metrics. 
* 
* @author istepanov 
*/ 
@Component 
public class SomeMetrics { 

    @Value("${const.metrics.some.connections.current:some.connections.created}") 
    private String connectorsCurrent; 
    @Value("${const.metrics.some.connections.idle:some.connections.idle}") 
    private String connectorsIdle; 
    @Value("${const.metrics.some.connections.max:some.connections.max}") 
    private String connectorsMax; 

    private CounterService counterService; 
    private GaugeService gaugeService; 
    private SomeProperties someProperties; 

    @Autowired 
    public void setSomeProperties(SomeProperties someProperties) { 
     this.someProperties = someProperties; 
    } 

    @Autowired 
    public void setCounterService(CounterService counterService) { 
     this.counterService = counterService; 
    } 

    @Autowired 
    public void setGaugeService(GaugeService gaugeService) { 
     this.gaugeService = gaugeService; 
    } 

    /** 
    * Use mocks for {@link CounterService} and {@link GaugeService} if CRMBO is not configured properly. 
    */ 
    @PostConstruct 
    public void init() { 
     if (someProperties.isMock()) { 
      counterService = mock(CounterService.class); 
      gaugeService = mock(GaugeService.class); 
     } 
    } 

    public void decrementConnectorsCurrent() { 
     this.counterService.decrement(connectorsCurrent); 
    } 

    public void incrementConnectorsCurrent() { 
     this.counterService.increment(connectorsCurrent); 
    } 

    public void decrementConnectorsIdle() { 
     this.counterService.decrement(connectorsIdle); 
    } 

    public void incrementConnectorsIdle() { 
     this.counterService.increment(connectorsIdle); 
    } 

    public void decrementConnectorsMax() { 
     this.counterService.decrement(connectorsMax); 
    } 

    public void incrementConnectorsMax() { 
     this.counterService.increment(connectorsMax); 
    } 
} 
+0

Beanの1つで '@ ConditionalOnMissingBean'アノテーションを削除するとどうなりますか? –

+0

変更なし - 同じ例外。 – stepio

+0

私が理解しているように、 '@ComponentScan(" org.project.module.support ")'というコードは無視されるので、低レベルのBeanは作成されません。 – stepio

答えて

1

これは、より多くの私には、通常のSpring構成の問題で、必ずしも春ブート自動構成1のように見えます。

SomeAutoConfigurationはアプリケーションコンテキストを初期化するために設定したもので、@ComponentScanアノテーションのおかげでSomeMetricsのような他のコンポーネントを見つけることができます。 SomeMetricsは、特定のSpring Boot Actuator Beanが存在することを前提としています。これは、テストでの狭いコンテキスト構成のためではありません。

必要な豆はそうのように、存在しない限り、あなたは物事が作成されてからそれを防ぐために働く、またはおSomeMetricsコンポーネントにいくつかの条件を追加したい場合は、コンテキストに多くの豆を追加する必要があります:

@Component 
@ConditionalOnBean({CounterService.class, GaugeService.class}) 
public class SomeMetrics { 

    // Content ommitted for brevity. 
} 

状況に適したソリューションがわかりません。

+0

おかげで、@ThomasKåsene。 @AndyWilkinsonの提案どおりにすべてのBeanを作成しようとしていて、間違いなく '@ ConditionalOnBean'で遊んでいます。 – stepio

+0

しかし、実際には、 '@ ConditionalOnBean'は、注文するのではなく、Beanを作成するかどうかを判断するために使用されると考えました。すべてのBeanをインスタンス化した後に注入が実行されるので、これは問題ではありません。 – stepio

+0

Beanを作成するかどうかを判断するために使用されます。つまり、あなたのテストでは設定されていないため、あなたのコンテキストに 'SomeMetrics'が必要とするBeanを含んでいなくても意味があります。自動構成のためにすべてのBeanを手動で作成しなければならないことに私は同意しますが、この場合の問題の原因とは思われません。 –

2

自動設定クラスのSpring Boot独自のテストからインスピレーションを得てみませんか?たとえば、JacksonAutoConfigurationTestsです。

自動構成クラスをテストするときには、コンテキスト内の異なるBeanと構成プロパティを使用してテストし、@ConditionalOnMissingBeanまたは@ConditionalOnPropertyの注釈が期待通りに機能することを確認できるようにします。このため、テストでは、@SpringBootTestまたはSpring Frameworkのテストフレームワークを使用していないため、クラス内のすべてのテストで同じアプリケーションコンテキストを使用する必要があります。

また、私は自動構成クラスで@ComponentScanを使用しないでください。 SpringBootの自動設定のどれもそれを使用しません。代わりに、自動構成では@Beanメソッドを使用してすべてのコンポーネントを定義するか、@Importおよび@ImportResourceをそれぞれ使用して、JavaおよびXMLベースの他の設定をそれぞれインポートする必要があります。

+0

ありがとう、私はちょうど最も簡単な方法を試して、それは働いた:)罪悪感。しかし、私は '@ ComponentScan'を取り除くでしょう - おそらくそれは問題を解決するでしょう。 – stepio

+0

Spring Bootにマイナーな改善提案:https://github.com/spring-projects/spring-boot/issues/6794 – stepio

関連する問題