I want configure a component test using spring-test configuration inner class (@Configuration
). Tested components has some services which I'd like to mock for the test. These services are classes (no interface used) and have spring annotations (@Autowired
) in them. Mockito can easily mock them, however, I found no way of disabling spring autowiring.
Example how I can easily reproduce:
@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; }
In this example "TestBean" represents the service to be mocked. I would NOT like "bar" to be injected by spring! @Bean(autowire = NO)
does not help (in fact, that's the default value). (Please save me from "use interfaces!" comments - the mocked service can be 3rd party which I can't do anything with.)
UPDATE
Springockito partially solves the problem, as long as you don't have to have anything else to configure (so you can't use configuration class with Springockito - it does not support it), but use mocks only. Still looking for pure spring solution, if there's any...
If you want to make specific bean autowiring non-mandatory for a specific bean property, use required=”false” attribute in @Autowired annotation.
You can exclude a bean from autowiring in Spring framework per-bean basis. If you are using Spring XML configuration then you can exclude a bean from autowiring by setting the autowire-candidate attribute of the <bean/> element to false.
You can just remove the @Autowired annotation from the constructor and it will still work (if you're not using a really old version of Spring).
To fix it, you can disable this checking feature by setting the “required” attribute of @Autowired to false. In the above example, if the Spring can't find a matching bean, it will leave the person property unset.
Here is my solution to your problem:
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(); } } }
and then just
@Import(MockitoSkipAutowireConfiguration.class)
in your test @Configuration
and you are all set
I solved it by creating FactoryBean for my bean instead of just mocking bean. At this way Spring don't try to autowire fields.
Factory bean helping class:
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; } }
Actual test context part:
@Configuration public class TestContext { @Bean public FactoryBean<MockingService> mockingService() { return new MockitoFactoryBean<>(MockingService.class); } }
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With