我想使用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"代表要嘲笑的服务。我不喜欢" bar"被春天注入! @Bean(autowire = NO)
无效(实际上,这是默认值)。
(请保存我从#34;使用界面!"评论 - 模拟服务可以是第三方,我无法做任何事情。)
更新
Springockito可以部分解决问题,只要您不必配置任何其他东西(因此您无法使用Springockito配置类 - 它不支持它),但仅使用模拟。 还在寻找纯粹的弹簧解决方案,如果有的话......
答案 0 :(得分:13)
以下是我解决问题的方法:
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
中,你已经完成了
答案 1 :(得分:5)
我通过为我的bean创建FactoryBean而不是仅仅模拟bean来解决它。通过这种方式,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);
}
}
答案 2 :(得分:2)
检查Spring profiles。您不需要禁用自动布线,您需要为不同的配置注入不同的bean。
答案 3 :(得分:2)
您可以通过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;
}
}
答案 4 :(得分:1)
我处于相同的情况。
我发现如果你没有在测试类上通过@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是值得的。
干杯,
罗伯特
答案 5 :(得分:0)
有很多方法可以做到这一点,我很确定这个答案是不完整的,但这里有几个选项......
由于目前似乎是推荐的做法,因此请为您的服务使用构造函数注入,而不是直接自动装配字段。这使得像这样的测试变得更加容易。
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之间切换。