Trying to mock a @SessionScope in a test by autowiring it using @SpyBean and then mocking using doReturn(..).when(sessionScopedBean).method(any()) calls the real method with null argument instead of silently registering mock. Removing @SessionScope from the bean resolves this issue.

Spring boot 2.1.6.RELEASE

Comment From: mbhave

@cdalexndr Please provide a minimal sample that we can use to reproduce the issue.

Comment From: cdalexndr

https://github.com/cdalexndr/spring-boot-issue-17817 Just run gradlew test Note that removing @SessionScope fixes test.

Comment From: blindpirate

I did some debugging. Seems like that @SessionScope is enhanced by Spring so every method in it becomes final, thus Mockito can't enhance it again. I'm wondering, why does Spring make all methods final when enhancing the bean? Can we somehow only make the injected methods final, but keep all declared methods non-final?

Comment From: wilkinsona

Thanks for the sample, @cdalexndr.

When you use @SpyBean, the spy is created via SpyPostProcessor which is an InstantiationAwareBeanPostProcessorAdapter and it is PriorityOrdered with highest precedence. This is intended to ensure that the spy is created before any proxying performed by Spring Framework. It works for @Scheduled, @Async, etc, but it does not work in the case of a scoped proxy as the proxying is done as part of component scanning rather than bean post-processing. Switching to a @Bean method on a @Configuration class does not help as ConfigurationClassBeanDefinitionReader does the same thing.

I can't see a way to fix this without some changes in Spring Framework. Flagging for team attention in case I've missed something.

Comment From: wilkinsona

We might be able to get the bean definition for the target as both it and the scoped proxy are added to the bean factory. We could perhaps then replace the target with the definition for the spy.

Comment From: wilkinsona

@cdalexndr While we're working on a fix, you can work around the problem by spying upon the scoped proxy's target which is the underlying SessionScopeBean instance that has not be proxied and, therefore, can be spied upon:

@SpyBean(name="scopedTarget.sessionScopedBean")

Comment From: elxnis

Hi @wilkinsona , could your fix for this cause issues with @SpyBean on @StepScoped beans? I have spring boot application that uses spring batch, so I have couple beans that are @StepScoped and I am using @SpyBean on them in my tests, but they have started to fail since updating to spring boot 2.1.9.RELEASE from 2.1.7.RELEASE.

Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'scopedTarget.beanName': Scope 'step' is not active for the current thread; consider defining a scoped proxy for this bean if you intend to refer to it from a singleton; nested exception is java.lang.IllegalStateException: No context holder available for step scope

Comment From: wilkinsona

That looks possible. It's certainly in the same area. Please open a new issue with a small sample that reproduces the issue and we can take a look.

Comment From: wilkinsona

Please note that, unfortunately, we've had to revert the fix for this issue. The problem can be avoided by adding a dependency on org.mockito:mockito-inline to your application's test dependencies.

Comment From: cdalexndr

@wilkinsona if that dependency fixes this issue, shouldn't be added to spring-boot-starter-test to be general available?

Comment From: wilkinsona

No, I don't think so. It fundamentally changes the way in which Mockito behaves and the Mockito team have chosen to make it opt-in for that reason. If Mockito makes it the default in the future then we would probably do the same in Spring Boot. Until then, I think it should remain opt-in so that users have a natural point at which to consider the implications.

Comment From: cdalexndr

@wilkinsona then if the fix was reverted and the workaround cannot be made general available due to side effects, shouldn't this issue be reopened, as a reminder, until a new fix is made?

Comment From: wilkinsona

We've already made the only "fix" that we can, and that is to add a note to the documentation about mockito-inline.