Is it possible to override a bean created through the @FeignClient
annotation by just creating a @Configuration
bean that contains a mocked version of it for testing?
I've already tried it but it seems the @FeignClient
bean is created last (or so I think) as in my test I always get injected the real version instead of the mocked one. In the same config file I have another bean created without any annotations (apart from @Component
) mocked the same way by just using the name of the real one and it works perfectly.
I've tried using @MockBean
to mock it and it works but the project has some quirks on it that make the creation of another Spring context break the tests.
Thanks.
EDIT. Actually, I just debugged the test and realised that, if I use the same name as the Feign client, the debugger doesn't even stop in the @Configuration
bean to create the mocked version. Changing the name to something else it works but it just creates another bean of the same type with the new name. Is there any configuration I'm missing here?
EDIT 2. This is an example code. Executing this I have that BarService
is the mocked version but FooService
is the real one.
@FeignClient(name = "fooService")
public interface FooService {
}
@Component
public class BarService {
}
@Configuration
public class ConfigClass {
@Bean
public FooService fooService() {
return Mockito.mock(FooService.class);
}
@Bean
public BarService barService() {
return Mockito.mock(BarService.class);
}
@RunWith(SpringRunner.class)
@ActiveProfiles("test")
@SpringBootTest
public class TestClass {
@Autowired
private FooService fooService;
@Autowired
private BarService barService;
@Test
public void test() {
System.out.println(fooService.getClass());
}
}
Your problem is caused by the fact that FeignClient beans are defined as primary
like declaring a bean with @Primary
. So it has a priority over other normal beans.
From spring-cloud-netflix 1.3.0 that is included from Dalston release, you can turn off this primary configuration like below.
@FeignClient(name = "fooService", primary = false)
public interface FooService {
}
If you change your code like above, Mocked bean will be injected into your test.
One thing you need to be careful is that primary
option is used when you are using fallback bean for your FeignClient. If you have fallback bean, you might need to specify FeignClient bean with qualifer to get FeignClient bean over fallback bean.
I think that another way to inject mocked bean instead of FeignClient bean for the test is using BeanPostProcessor
something like below.
public static class MockProcessor implements BeanPostProcessor {
:
:
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (/* distinguish your target bean */)
return Mockito.mock(FooService.class);
return bean;
}
}
The annotation @EnableFeignClients
will create primary beans for interfaces marked with @FeignClient
by default. Disabling this for every interface may have side effects, like when there are fallback beans.
In order to prevent the creation of the Feign beans in the first place, just move this annotation to a configuration class which is disabled for tests.
@Configuration
@Profile("!test")
@EnableFeignClients
public class FeignEnable {
}
Then annotation your test class with @ActiveProfiles("test")
.
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