Como usar o @ComponentScan junto com ContextConfigurations específicos de teste no SpringJunit4TestRunner?

Estou testando um aplicativo Spring Boot. Eu tenho várias classes de teste, cada uma das quais precisa de um conjunto diferente de beans simulados ou personalizados.

Aqui está um esboço da configuração:

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...
    }
}

O problema com a configuração acima é que a varredura de componente configurada no MyApplication seleciona Component1TestWithFakeCommunication.ContextConfiguration, para que eu obtenha um ServiceClient simulado, mesmo no Component1TestWithRealCommunication, onde desejo a implementação real do ServiceClient.

Embora eu possa usar os construtores @Autowired e criar os componentes em ambos os testes, há uma quantidade suficiente de coisas com configurações complicadas que eu preferiria que o Spring TestContext fosse configurado para mim (por exemplo, repositórios Spring Data JPA, componentes de bibliotecas fora do aplicativo que extrai beans do contexto do Spring etc.). Aninhar uma configuração do Spring dentro do teste que pode substituir localmente determinadas definições de bean no contexto do Spring parece que deve ser uma maneira limpa de fazer isso; a única queda é que essas configurações aninhadas acabam afetando todos os testes Spring TestContext que baseiam sua configuração no MyApplication (qual componente verifica o pacote de aplicativos).

Como modifico minha configuração para que eu ainda tenha um contexto do Spring "principalmente real" para meus testes com apenas alguns beans substituídos localmente em cada classe de teste?

questionAnswers(4)

yourAnswerToTheQuestion