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コンテキストを取得するために、セットアップを変更するにはどうすればよいですか?
以下は、新しいfake-communication
profileこれは現在のテストクラスにのみ適用可能です。
@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);
}
}
}
@SpringBootTest
がある場合は、@MockBean
でモックするサービスに注釈を付けることができます。それと同じくらい簡単です。
追加の明示的プロファイルを使用して、このようなテスト構成がピックアップされるのを回避できます(別の回答で提案されています)。私もそれをし、そのためのライブラリサポートを作成しました。
ただし、Spring-Bootは巧妙で、この問題を自動的に解決する組み込みの「タイプフィルター」があります。これが機能するためには、テスト構成を見つける@ComponentScan
注釈を削除し、@SpringBootApplication
に作業をさせる必要があります。あなたの例では、これを削除するだけです:
@SpringBootApplication
@ComponentScan(
basePackageClasses = {
MyApplication.class,
ImportantConfigurationFromSomeLibrary.class,
ImportantConfigurationFromAnotherLibrary.class})
それを次のものに置き換えます:
@SpringBootApplication(scanBasePackageClasses= {
MyApplication.class,
ImportantConfigurationFromSomeLibrary.class,
ImportantConfigurationFromAnotherLibrary.class})
テストに@SpringBootTest
の注釈を付ける必要がある場合もあります。これにより、currentテストにあるものを除き、内部クラスの構成(およびコンポーネント)の自動スキャンが回避されます。
私はいくつかのことをします:
@ComponentScan
_ ingしないようにします。Component1TestWithFakeCommunication
_で、@SpringApplicationConfiguration(classes = MyApplication.class)
を@SpringApplicationConfiguration(classes = {MyApplication.class, Component1TestWithFakeCommunication.ContextConfiguration.class})
に変更しますこれにより、SpringはテストBeanをモックアップするのに十分な情報を提供するはずですが、ランタイムApplicationContext
もテストBeanに気付かないようにする必要があります。