web-dev-qa-db-ja.com

@ Configuration / @ Beanの使用の単体テストでSpring自動配線を無効にする方法

スプリングテスト構成内部クラス(_@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で設定クラスを使用できないため、サポートしていません)、問題を部分的に解決しますが、モックのみを使用します。まだ純粋な春の解決策を探しています、もしあれば...

17
vuk

これがあなたの問題に対する私の解決策です:

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以上で準備完了です

14

単に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);
    }
}
6
tr1cks

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;

    }
}
2
Oliver

チェック スプリングプロファイル 。自動ワイヤリングを無効にする必要はありません。構成ごとに異なるBeanを注入する必要があります。

2
Adi

私もまったく同じ状況です。

テストクラスの@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を適切に実装する方法を見つけるためにより多くの時間を費やすことは価値があります。

乾杯、
ロバート

1
Robert

これを行う方法はたくさんありますが、この答えは不完全だと思いますが、ここにいくつかのオプションがあります...

現在が推奨されるプラクティスであると思われるため、フィールドを直接自動配線するのではなく、サービスにコンストラクター注入を使用します。これにより、このようなテストが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を切り替えることができます。

0
Steve