web-dev-qa-db-ja.com

SpringJunit4TestRunnerで@ComponentScanをテスト固有のContextConfigurationsと共に使用する方法は?

Spring Bootアプリケーションをテストしています。いくつかのテストクラスがあり、それぞれに異なるセットのモックまたはカスタマイズされたBeanが必要です。

セットアップのスケッチは次のとおりです。

src/main/Java:

package com.example.myapp;

@SpringBootApplication
@ComponentScan(
        basePackageClasses = {
                MyApplication.class,
                ImportantConfigurationFromSomeLibrary.class,
                ImportantConfigurationFromAnotherLibrary.class})
@EnableFeignClients
@EnableHystrix
public class MyApplication {
    public static void main(String[] args) {
        SpringApplication.run(MyApplication.class, args);
    }
}

package com.example.myapp.feature1;

@Component
public class Component1 {
    @Autowired
    ServiceClient serviceClient;

    @Autowired
    SpringDataJpaRepository dbRepository;

    @Autowired
    ThingFromSomeLibrary importantThingIDontWantToExplicitlyConstructInTests;

    // methods I want to test...
}

src/test/Java:

package com.example.myapp;

@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = MyApplication.class)
@WebAppConfiguration
@ActiveProfiles("test")
public class Component1TestWithFakeCommunication {

    @Autowired
    Component1 component1; // <-- the thing we're testing. wants the above mock implementations of beans wired into it.

    @Autowired
    ServiceClient mockedServiceClient;

    @Configuration
    static class ContextConfiguration {
        @Bean
        @Primary
        public ServiceClient mockedServiceClient() {
            return mock(ServiceClient.class);
        }
    }

    @Before
    public void setup() {
        reset(mockedServiceClient);
    }

    @Test
    public void shouldBehaveACertainWay() {
        // customize mock, call component methods, assert results...
    }
}

package com.example.myapp;

@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = MyApplication.class)
@WebAppConfiguration
@ActiveProfiles("test")
public class Component1TestWithRealCommunication {

    @Autowired
    Component1 component1; // <-- the thing we're testing. wants the real implementations in this test.

    @Autowired
    ServiceClient mockedServiceClient;

    @Before
    public void setup() {
        reset(mockedServiceClient);
    }

    @Test
    public void shouldBehaveACertainWay() {
        // call component methods, assert results...
    }
}

上記のセットアップの問題は、MyApplicationで構成されたコンポーネントスキャンがComponent1TestWithFakeCommunication.ContextConfigurationを取得するため、実際のServiceClientの実装が必要なComponent1TestWithRealCommunicationでもモックServiceClientを取得することです。

どちらのテストでも@Autowiredコンストラクターを使用してコンポーネントを自分で構築できますが、Spring TestContextをセットアップするよりもかなり複雑な設定のものがあります(たとえば、Spring Data JPAリポジトリ、ライブラリーのコンポーネントSpringコンテキストからBeanを取得するアプリの外部など)。 Springコンテキスト内の特定のBean定義をローカルでオーバーライドできるテスト内にSpring構成をネストすることは、これを行うためのクリーンな方法であると思われます。唯一の欠点は、これらのネストされた構成が、MyApplication(アプリケーションパッケージをスキャンするコンポーネント)に基づいて構成されたすべてのSpring TestContextテストに影響を与えることです。

各テストクラスでローカルにオーバーライドされたいくつかのBeanのみを使用して、テスト用の「ほぼ実際の」Springコンテキストを取得するために、セットアップを変更するにはどうすればよいですか?

15
Jonathan Fuerth

以下は、新しいfake-communicationprofileこれは現在のテストクラスにのみ適用可能です。

@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = MyApplication.class)
@WebAppConfiguration
@ActiveProfiles({"test", "fake-communication"})
public class Component1TestWithFakeCommunication {

    // @Autowired ...

    @Profile("fake-communication")
    @Configuration
    static class ContextConfiguration {
        @Bean
        @Primary
        public ServiceClient mockedServiceClient() {
            return mock(ServiceClient.class);
        }
    }
}
12
Sam Brannen

@SpringBootTestがある場合は、@MockBeanでモックするサービスに注釈を付けることができます。それと同じくらい簡単です。

3
Jorge Viana

追加の明示的プロファイルを使用して、このようなテスト構成がピックアップされるのを回避できます(別の回答で提案されています)。私もそれをし、そのためのライブラリサポートを作成しました。

ただし、Spring-Bootは巧妙で、この問題を自動的に解決する組み込みの「タイプフィルター」があります。これが機能するためには、テスト構成を見つける@ComponentScan注釈を削除し、@SpringBootApplicationに作業をさせる必要があります。あなたの例では、これを削除するだけです:

@SpringBootApplication
@ComponentScan(
    basePackageClasses = {
            MyApplication.class,
            ImportantConfigurationFromSomeLibrary.class,
            ImportantConfigurationFromAnotherLibrary.class})

それを次のものに置き換えます:

@SpringBootApplication(scanBasePackageClasses= {
            MyApplication.class,
            ImportantConfigurationFromSomeLibrary.class,
            ImportantConfigurationFromAnotherLibrary.class})

テストに@SpringBootTestの注釈を付ける必要がある場合もあります。これにより、currentテストにあるものを除き、内部クラスの構成(およびコンポーネント)の自動スキャンが回避されます。

1
Marwin

私はいくつかのことをします:

  1. テストクラスを別のパッケージに移動して、_@ComponentScan_ ingしないようにします。
  2. _Component1TestWithFakeCommunication_で、@SpringApplicationConfiguration(classes = MyApplication.class)@SpringApplicationConfiguration(classes = {MyApplication.class, Component1TestWithFakeCommunication.ContextConfiguration.class})に変更します

これにより、SpringはテストBeanをモックアップするのに十分な情報を提供するはずですが、ランタイムApplicationContextもテストBeanに気付かないようにする必要があります。

0
Josh Ghiloni