[英]How to disable Spring autowiring in unit tests for @Configuration/@Bean usage
我想使用spring-test配置內部類( @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)
沒有幫助(實際上,這是默認值)。 (請保存我從“使用界面!”評論 - 模擬服務可以是第三方,我無法做任何事情。)
UPDATE
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創建FactoryBean而不是僅僅模擬bean來解決它。 這樣Spring就不會嘗試自動裝載字段。
工廠豆幫助班:
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);
}
}
檢查Spring配置文件 。 您不需要禁用自動布線,您需要為不同的配置注入不同的bean。
您可以通過org.springframework.beans.factory.config.SingletonBeanRegistry #registerSingleton手動將模擬服務添加到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;
}
}
我處於相同的情況。
我發現如果你沒有在測試類上通過@ContextConfiguration注釋設置上下文加載器,將使用默認的上下文加載器,它派生自AbstractGenericContextLoader。 我查看了它的源代碼,結果發現它注冊了所有負責讀取@Autowired注釋的bean后處理器。 換句話說,默認情況下啟用注釋配置。
所以主要的問題是有兩種配置存在沖突:在java配置中我們說不需要自動裝配,而自動裝配的注釋則相反。 真正的問題是如何禁用注釋處理以消除不需要的配置。
據我所知,沒有這樣的SpringLoader的Spring實現,它不會從AbstractGenericContextLoader派生出來,所以我想我們唯一能做的就是編寫自己的。 它會是這樣的:
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是值得的。
干杯,
羅伯特
有很多方法可以做到這一點,我很確定這個答案是不完整的,但這里有幾個選擇......
目前似乎是推薦的做法,為您的服務使用構造函數注入,而不是直接自動裝配字段。 這使得像這樣的測試就輕松多了。
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。
一個合理的替代方法是使用Spring配置文件來定義存根服務。 當希望在多個測試中使用相同的存根特征時,這尤其有用:
@Service
@Primary
@Profile("test")
public class MyServiceStub implements MyService {
// ...
}
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = SomeTest.Beans.class)
@ActiveProfiles({"test"})
public class SomeTest {
// ...
}
通過使用@Primary
注釋,它確保將使用此存根bean而不是實現MyService
接口的任何其他bean。 我傾向於使用這種方法來處理電子郵件服務,通過更改配置文件,我可以在真正的郵件服務器和Wiser之間切換。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.