如何在SpringJunit4TestRunner中将@ComponentScan与特定于测试的ContextConfiguration一起使用?

Jon*_*rth 15 spring spring-test spring-boot

我正在测试一个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...
}
Run Code Online (Sandbox Code Playgroud)

的src /测试/ 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...
    }
}
Run Code Online (Sandbox Code Playgroud)

上述设置的问题在于MyApplication中配置的组件扫描会选择Component1TestWithFakeCommunication.ContextConfiguration,因此我甚至在Component1TestWithRealCommunication中获得了一个模拟ServiceClient,我想要真正的ServiceClient实现.

虽然我可以在两个测试中使用@Autowired构造函数并自己构建组件,但是有足够数量的东西具有复杂的设置,我宁愿为我设置Spring TestContext(例如,Spring Data JPA存储库,来自库的组件)在应用程序之外,从Spring上下文中拉豆等).将Spring配置嵌套在可以在Spring上下文中本地覆盖某些bean定义的测试中,感觉它应该是一种干净的方式来执行此操作; 唯一的缺点是这些嵌套配置最终会影响所有Spring TestContext测试,这些测试基于MyApplication(该组件扫描应用程序包)的配置.

我如何修改我的设置,所以我仍然得到一个"大多数真正的"Spring上下文,我的测试只有几个本地重写的bean在每个测试类中?

Sam*_*nen 12

以下内容应通过引入仅适用于当前测试类的新fake-communication 配置文件来帮助您实现目标.

@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);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 这是一个可行的解决方案,但有点“变通”,也有点不方便。Spring Boot 通过其自动排除过滤器提供了更好的方法。附在不同答案中的示例。 (2认同)

Mar*_*win 5

您可以使用其他显式配置文件来避免选择此类测试配置(如另一个答案中所建议)。我也这样做了,甚至为此创建了一些库支持。

然而,Spring-Boot 很聪明,它有一个内置的“类型过滤器”来自动解决这个问题。为此,您需要删除您的@ComponentScan注释,它会找到您的测试配置,并让其@SpringBootApplication完成工作。在您的示例中,只需删除此内容:

@SpringBootApplication
@ComponentScan(
    basePackageClasses = {
            MyApplication.class,
            ImportantConfigurationFromSomeLibrary.class,
            ImportantConfigurationFromAnotherLibrary.class})
Run Code Online (Sandbox Code Playgroud)

并将其替换为:

@SpringBootApplication(scanBasePackageClasses= {
            MyApplication.class,
            ImportantConfigurationFromSomeLibrary.class,
            ImportantConfigurationFromAnotherLibrary.class})
Run Code Online (Sandbox Code Playgroud)

您可能还需要将您的测试注释为@SpringBootTest. 这应该避免自动扫描任何内部类配置(和组件),除了驻留在当前测试中的那些。


Jor*_*ana 5

如果你有一个,@SpringBootTest你可以只注释你想要模拟的服务@MockBean。就如此容易。