スプリングテスト構成内部クラス(_@Configuration
_)を使用してコンポーネントテストを構成したい。テスト済みコンポーネントには、テストのために模擬したいいくつかのサービスがあります。これらのサービスはクラスであり(インターフェースは使用されません)、Springアノテーション(_@Autowired
_)が含まれています。 Mockitoは簡単にそれらをあざけることができますが、春の自動配線を無効にする方法は見つかりませんでした。
簡単に再現できる例:
_@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = SomeTest.Beans.class)
public class SomeTest {
// configured in component-config.xml, using ThirdPartyService
@Autowired
private TestedBean entryPoint;
@Test
public void test() {
}
@Configuration
@ImportResource("/spring/component-config.xml")
static class Beans {
@Bean
ThirdPartyService createThirdPartyService() {
return mock(ThirdPartyService.class);
}
}
}
public class ThirdPartyService {
@Autowired
Foo bar;
}
public class TestedBean {
@Autowired
private ThirdPartyService service;
}
_
この例では、「TestBean」はモックされるサービスを表します。春までに「バー」を注入したくない! @Bean(autowire = NO)
は役に立ちません(実際、それがデフォルト値です)。 (「インターフェースを使用してください!」コメントから私を救ってください-モックされたサービスは、私が何もできないサードパーティになる可能性があります。)
[〜#〜]更新[〜#〜]
Springockitoは、他に設定する必要がない限り(Springockitoで設定クラスを使用できないため、サポートしていません)、問題を部分的に解決しますが、モックのみを使用します。まだ純粋な春の解決策を探しています、もしあれば...
これがあなたの問題に対する私の解決策です:
import static org.mockito.Mockito.mockingDetails;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessorAdapter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class MockitoSkipAutowireConfiguration {
@Bean MockBeanFactory mockBeanFactory() {
return new MockBeanFactory();
}
private static class MockBeanFactory extends InstantiationAwareBeanPostProcessorAdapter {
@Override
public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
return !mockingDetails(bean).isMock();
}
}
}
そしてそれから
@Import(MockitoSkipAutowireConfiguration.class)
テスト中@Configuration
以上で準備完了です
単にBeanをあざけるのではなく、私のBeanにFactoryBeanを作成することで解決しました。このように、Springはフィールドを自動配線しようとしません。
ファクトリーBean支援クラス:
public class MockitoFactoryBean<T> implements FactoryBean<T> {
private final Class<T> clazz;
public MockitoFactoryBean(Class<T> clazz) {
this.clazz = clazz;
}
@Override public T getObject() throws Exception {
return mock(clazz);
}
@Override public Class<T> getObjectType() {
return clazz;
}
@Override public boolean isSingleton() {
return true;
}
}
実際のテストコンテキスト部分:
@Configuration
public class TestContext {
@Bean
public FactoryBean<MockingService> mockingService() {
return new MockitoFactoryBean<>(MockingService.class);
}
}
Org.springframework.beans.factory.config.SingletonBeanRegistry#registerSingletonを使用して、模擬サービスを手動でSpringアプリケーションコンテキストに追加できます。このようにして、モックはSpringによって後処理されず、Springはモックの自動配線を試みません。モック自体がテスト済みBeanに注入されます。
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = SomeTest.Beans.class)
public class SomeTest {
// configured in component-config.xml, using ThirdPartyService
@Autowired
private TestedBean entryPoint;
@Autowired
private ThirdPartyService thirdPartyServiceMock;
@Test
public void test() {
}
@Configuration
static class Beans {
@Autowired
private GenericApplicationContext ctx;
@Bean
TestedBean testedBean() {
ctx.getBeanFactory().registerSingleton("thirdPartyService", mock(ThirdPartyService.class));
return new TestedBean();
}
}
public static class ThirdPartyService {
@Autowired
Object bar;
}
public static class TestedBean {
@Autowired
private ThirdPartyService service;
}
}
チェック スプリングプロファイル 。自動ワイヤリングを無効にする必要はありません。構成ごとに異なるBeanを注入する必要があります。
私もまったく同じ状況です。
テストクラスの@ContextConfigurationアノテーションでコンテキストローダーを設定しない場合、AbstractGenericContextLoaderから派生したデフォルトのコンテキストローダーが使用されることがわかりました。私はそのソースを見て、@ Autowiredなどの注釈の読み取りを担当するすべてのBeanポストプロセッサを登録していることがわかりました。つまり、アノテーション設定はデフォルトで有効になっています。
したがって、主な問題は、競合する2つの構成があることです。Java構成では、自動配線は不要であるのに対し、自動配線されたアノテーションは反対を示します。実際の問題は、不要な構成を排除するために、注釈処理を無効にします。
私が知る限り、AbstractGenericContextLoaderから派生しないContextLoaderのそのような春の実装はないので、私たちができる唯一のことは、独自のものを書くことだと思います。これは次のようなものです。
public static class SimpleContextLoader implements ContextLoader {
@Override
public String[] processLocations(Class<?> type, String... locations) {
return strings;
}
@Override
public ApplicationContext loadContext(String... locations) throws Exception {
// in case of xml configuration
return new ClassPathXmlApplicationContext(strings);
// in case of Java configuration (but its name is quite misleading)
// return new AnnotationConfigApplicationContext(TestConfig.class);
}
}
もちろん、ContextLoaderを適切に実装する方法を見つけるためにより多くの時間を費やすことは価値があります。
乾杯、
ロバート
これを行う方法はたくさんありますが、この答えは不完全だと思いますが、ここにいくつかのオプションがあります...
現在が推奨されるプラクティスであると思われるため、フィールドを直接自動配線するのではなく、サービスにコンストラクター注入を使用します。これにより、このようなテストがsoより簡単になります。
public class SomeTest {
@Mock
private ThirdPartyService mockedBean;
@Before
public void init() {
initMocks(this);
}
@Test
public void test() {
BeanUnderTest bean = new BeanUnderTest(mockedBean);
// ...
}
}
public class BeanUnderTest{
private ThirdPartyService service;
@Autowired
public BeanUnderTest(ThirdPartyService ThirdPartyService) {
this.thirdPartyService = thirdPartyService;
}
}
そうすることで、テスト自体に自動配線し、自動配線されたBeanと模擬されたBeanの最も便利な組み合わせでテスト中のBeanを構築することで、自動配線されたサービスと模擬されたサービスを混在させることもできます。
妥当な代替策は、Springプロファイルを使用してスタブサービスを定義することです。これは、複数のテストで同じスタブ化された機能を使用する場合に特に便利です。
@Service
@Primary
@Profile("test")
public class MyServiceStub implements MyService {
// ...
}
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = SomeTest.Beans.class)
@ActiveProfiles({"test"})
public class SomeTest {
// ...
}
@Primary
アノテーションは、MyService
インターフェースを実装する他のBeanの代わりにこのスタブBeanが使用されることを保証します。私はこのアプローチをメールサービスのようなものに使用する傾向があり、プロファイルを変更することで、実際のメールサーバーとWiserを切り替えることができます。