Skip to content

MockBean behaviour for Feign Clients change since Spring Boot 2.2.7 Release #336

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
MPoorter opened this issue May 9, 2020 · 4 comments
Closed

Comments

@MPoorter
Copy link

MPoorter commented May 9, 2020

Bug
Versions:
Spring Boot: 2.2.7
Spring Cloud: Hoxton.SR4

Before the Spring Boot 2.2.7 release, we used to create a Feign client (no fallback needed) and in certain situations we needed to create a MockBean of the Feign client to test. This worked fine; the mocked feign client overruled the actual feign client (even if it was marked as primary by default). Since the Spring Boot 2.2.7 release, this does not work anymore.

Other beans marked as @Primary continue to be replaced by a mocked bean, making me believe this is specific to the implementation of Feign Clients and not a problem higher up in the Spring Boot eco system.

We have been able to trace it back to this change in the 2.2.7 Release:
spring-projects/spring-boot@b9c2b7b#diff-e5e8e2d1fb541676ad4921f5e52f5f55L250

Sample

@FeignClient(value = "random-test")
public interface RandomClient {

    @GetMapping(value = "/random-test", produces = APPLICATION_JSON_VALUE)
    String getRandomString();

}
@Service
public class TestService {

    @Autowired
    private RandomClient randomClient;

    public String testMethod() {
        return randomClient.getRandomString();
    }
}
@RunWith(SpringRunner.class)
@SpringBootTest(properties = "spring.profiles.active=test")
public class TestServiceTest {

    @MockBean
    private RandomClient randomClient;

    @Autowired
    private TestService testService;

    @Test
    public void randomClientShouldBeMocked() {
        when(randomClient.getRandomString()).thenReturn("Mocked Feign Client");
        System.out.println(testService.testMethod());
    }
}

This now throws the following error since Spring Boot 2.2.7 Release:

Caused by: com.netflix.client.ClientException: Load balancer does not have available server for client: random-test
	at com.netflix.loadbalancer.LoadBalancerContext.getServerFromLoadBalancer(LoadBalancerContext.java:483)
	at com.netflix.loadbalancer.reactive.LoadBalancerCommand$1.call(LoadBalancerCommand.java:184)
	at com.netflix.loadbalancer.reactive.LoadBalancerCommand$1.call(LoadBalancerCommand.java:180)
	at rx.Observable.unsafeSubscribe(Observable.java:10327)
	at rx.internal.operators.OnSubscribeConcatMap.call(OnSubscribeConcatMap.java:94)
	at rx.internal.operators.OnSubscribeConcatMap.call(OnSubscribeConcatMap.java:42)
	at rx.Observable.unsafeSubscribe(Observable.java:10327)
	at rx.internal.operators.OperatorRetryWithPredicate$SourceSubscriber$1.call(OperatorRetryWithPredicate.java:127)
	at rx.internal.schedulers.TrampolineScheduler$InnerCurrentThreadScheduler.enqueue(TrampolineScheduler.java:73)
	at rx.internal.schedulers.TrampolineScheduler$InnerCurrentThreadScheduler.schedule(TrampolineScheduler.java:52)
	at rx.internal.operators.OperatorRetryWithPredicate$SourceSubscriber.onNext(OperatorRetryWithPredicate.java:79)
	at rx.internal.operators.OperatorRetryWithPredicate$SourceSubscriber.onNext(OperatorRetryWithPredicate.java:45)
	at rx.internal.util.ScalarSynchronousObservable$WeakSingleProducer.request(ScalarSynchronousObservable.java:276)
	at rx.Subscriber.setProducer(Subscriber.java:209)
	at rx.internal.util.ScalarSynchronousObservable$JustOnSubscribe.call(ScalarSynchronousObservable.java:138)
	at rx.internal.util.ScalarSynchronousObservable$JustOnSubscribe.call(ScalarSynchronousObservable.java:129)
	at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:48)
	at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:30)
	at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:48)
	at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:30)
	at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:48)
	at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:30)
	at rx.Observable.subscribe(Observable.java:10423)
	at rx.Observable.subscribe(Observable.java:10390)
	at rx.observables.BlockingObservable.blockForSingle(BlockingObservable.java:443)
	at rx.observables.BlockingObservable.single(BlockingObservable.java:340)
	at com.netflix.client.AbstractLoadBalancerAwareClient.executeWithLoadBalancer(AbstractLoadBalancerAwareClient.java:112)
	at org.springframework.cloud.openfeign.ribbon.LoadBalancerFeignClient.execute(LoadBalancerFeignClient.java:83)
	... 32 more

Workaround
One workaround we found is marking the FeignClient itself as non-primary and adding the bean name to the mocked bean in order for it to be used instead of the actual Feign client.

@FeignClient(value = "random-test", primary = false)
public interface RandomClient {

    @GetMapping(value = "/random-test", produces = APPLICATION_JSON_VALUE)
    String getRandomString();

}
@RunWith(SpringRunner.class)
@SpringBootTest(properties = "spring.profiles.active=test")
public class TestServiceTest {

    @MockBean(name = "randomClient")
    private RandomClient randomClient;

    @Autowired
    private TestService testService;

    @Test
    public void randomClientShouldBeMocked() {
        when(randomClient.getRandomString()).thenReturn("Mocked Feign Client");
        System.out.println(testService.testMethod());
    }
}
@spencergibb
Copy link
Member

Feign has nothing to do with mock bean support so I hesitate to see how this is done change in behavior with feign.

@MPoorter
Copy link
Author

MPoorter commented May 9, 2020

@spencergibb True, but I could also imagine Spring Boot saying that they're not responsible for how Feign creates its beans.
The fact that I don't see this behaviour across the board, and especially that I don't see this behaviour for beans within my own projects that are marked as @Primary, makes me wonder whether something is going on with how a Feign Client bean is generated.
I'm happy to raise an issue against Spring Boot and see what they come back with, but my initial instinct told me that maybe something within the Feign implementation had gotten out of sync. Though, of course, not an expert and happy to be proven wrong :)

@spencergibb
Copy link
Member

Feign bean creation hasn't had major changes in a long time. Something in boot changes and there's a problem all of a sudden? Let's open there and if they say differently we can reopen.

@MPoorter
Copy link
Author

@spencergibb This would seem to be the issue: #337

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants