spring 如何在 SpringJunit4TestRunner 中将 @ComponentScan 与测试特定的 ContextConfigurations 一起使用?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/39300167/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to use @ComponentScan together with test-specific ContextConfigurations in SpringJunit4TestRunner?
提问by Jonathan Fuerth
I am testing a Spring Boot application. I have several test classes, each of which needs a different set of mocked or otherwise customized beans.
我正在测试 Spring Boot 应用程序。我有几个测试类,每个类都需要一组不同的模拟或以其他方式定制的 bean。
Here is a sketch of the setup:
这是设置的草图:
src/main/java:
源代码/主/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:
源代码/测试/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...
}
}
The problem with the above setup is that the component scan configured in MyApplication picks up Component1TestWithFakeCommunication.ContextConfiguration, so I get a mock ServiceClient even in Component1TestWithRealCommunication where I want the real ServiceClient implementation.
上述设置的问题在于,在 MyApplication 中配置的组件扫描选择了 Component1TestWithFakeCommunication.ContextConfiguration,所以即使在我想要真正的 ServiceClient 实现的 Component1TestWithRealCommunication 中,我也得到了一个模拟 ServiceClient。
Although I could use @Autowired constructors and build up the components myself in both tests, there is a sufficient amount of stuff with complicated setup that I would rather have Spring TestContext set up for me (for example, Spring Data JPA repositories, components from libraries outside the app that pull beans from the Spring context, etc.). Nesting a Spring configuration inside the test that can locally override certain bean definitions within the Spring context feels like it should be a clean way to do this; the only downfall is that these nested configurations end up affecting all Spring TestContext tests that base their configuration on MyApplication (which component scans the app package).
尽管我可以在两个测试中使用 @Autowired 构造函数并自己构建组件,但有足够多的东西具有复杂的设置,我宁愿为我设置 Spring TestContext(例如,Spring Data JPA 存储库、库中的组件)在从 Spring 上下文等中提取 bean 的应用程序之外)。在测试中嵌套一个 Spring 配置,它可以本地覆盖 Spring 上下文中的某些 bean 定义,感觉应该是一种干净的方式来做到这一点;唯一的缺点是这些嵌套配置最终会影响所有基于 MyApplication(该组件扫描应用程序包)的配置的 Spring TestContext 测试。
How do I modify my setup so I still get a "mostly real" Spring context for my tests with just a few locally overridden beans in each test class?
如何修改我的设置,以便在每个测试类中只用几个本地覆盖的 bean 为我的测试获得“大部分真实”的 Spring 上下文?
采纳答案by Sam Brannen
The following should help you to achieve your goal by introducing a new fake-communication
profilethat is applicable only to the current test class.
以下内容应通过引入仅适用于当前测试类的新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);
}
}
}
回答by Jorge Viana
If you have a @SpringBootTest
you can just annotate the service you want to mock with @MockBean
. As simple as that.
如果你有一个,@SpringBootTest
你可以只注释你想要模拟的服务@MockBean
。就如此容易。
回答by Marwin
You may use additional explicit profiles to avoid such test configurations to be picked up (as suggested in another answer). I also did it and even created some library support for that.
您可以使用其他显式配置文件来避免选择此类测试配置(如另一个答案中所建议)。我也这样做了,甚至为此创建了一些库支持。
However, Spring-Boot is clever and it has a built-in "type filter" to resolve this issue automatically. For this to work, you need to remove your @ComponentScan
annotation, which would find your test configurations, and let the @SpringBootApplication
do the work. In your example, just remove this:
然而,Spring-Boot 很聪明,它有一个内置的“类型过滤器”来自动解决这个问题。为此,您需要删除您的@ComponentScan
注释,它会找到您的测试配置,并让其@SpringBootApplication
完成工作。在您的示例中,只需删除此内容:
@SpringBootApplication
@ComponentScan(
basePackageClasses = {
MyApplication.class,
ImportantConfigurationFromSomeLibrary.class,
ImportantConfigurationFromAnotherLibrary.class})
and replace it with:
并将其替换为:
@SpringBootApplication(scanBasePackageClasses= {
MyApplication.class,
ImportantConfigurationFromSomeLibrary.class,
ImportantConfigurationFromAnotherLibrary.class})
You may also need to annotate your test as @SpringBootTest
. This should avoid auto-scanning any inner-class configurations (and components) except for those residing in the currenttest.
您可能还需要将您的测试注释为@SpringBootTest
. 这应该避免自动扫描任何内部类配置(和组件),除了驻留在当前测试中的那些。
回答by Josh Ghiloni
I would do a couple of things:
我会做几件事:
- Move your test classes into a different package to avoid
@ComponentScan
ing them. - In
Component1TestWithFakeCommunication
, change@SpringApplicationConfiguration(classes = MyApplication.class)
to@SpringApplicationConfiguration(classes = {MyApplication.class, Component1TestWithFakeCommunication.ContextConfiguration.class})
- 将您的测试类移动到不同的包中以避免
@ComponentScan
对它们进行ing。 - 在
Component1TestWithFakeCommunication
,@SpringApplicationConfiguration(classes = MyApplication.class)
改为@SpringApplicationConfiguration(classes = {MyApplication.class, Component1TestWithFakeCommunication.ContextConfiguration.class})
That should give Spring sufficient information to mock up the test beans, but it should prevent the runtime ApplicationContext
from noticing your test beans as well.
这应该为 Spring 提供足够的信息来模拟测试 bean,但它也应该防止运行时ApplicationContext
注意到您的测试 bean。