Skip to content
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

@SpyBean does not work when used to spy on a Spring Data Repository #7033

Closed
igormukhin opened this issue Sep 27, 2016 · 69 comments
Closed

@SpyBean does not work when used to spy on a Spring Data Repository #7033

igormukhin opened this issue Sep 27, 2016 · 69 comments
Assignees
Labels
type: bug A general bug
Milestone

Comments

@igormukhin
Copy link

igormukhin commented Sep 27, 2016

Version: Spring Boot 1.4.1
Subject: @SpyBean on Data Jpa Repository bean isn't working
Exception thrown:

UnsatisfiedDependencyException: Error creating bean with name 'cityService' defined in file [...\target\classes\com\example\CityService.class]: Unsatisfied dependency expressed through constructor parameter 0; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'cityRepository': Initialization of bean failed; nested exception is java.lang.IllegalArgumentException: Object of class [org.springframework.data.jpa.repository.support.JpaRepositoryFactoryBean] must be an instance of interface com.example.CityRepository

Demonstration project: https://github.com/igormukhin/spring-boot-issue-6871
USE BRANCH: spybean-on-jparepository

@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged label Sep 27, 2016
@igormukhin
Copy link
Author

Nobody cares 😢 @philwebb @wilkinsona @snicoll

@philwebb
Copy link
Member

We care, we're just snowed under at the moment!

The root of the problem is that SpyPostProcessor is used postProcessBeforeInitialization which is passed a FactoryBean and not the actual instance that needs mocking. I tried changing the process to use postProcessAfterInitialization:

        @Override
        public Object postProcessAfterInitialization(Object bean, String beanName)
                throws BeansException {
            if (bean instanceof FactoryBean) {
                return bean;
            }
            return createSpyIfNecessary(bean, beanName);
        }

This gets us further but doesn't fix the issue because Mockto isn't able to spy on the Proxy:

Cannot mock/spy class com.example.$Proxy81
Mockito cannot mock/spy following:
  - final classes
  - anonymous classes
  - primitive types
2016-10-10 19:11:11.065  INFO 63856 --- [           main] j.LocalContainerEntityManagerFactoryBean : Closing JPA EntityManagerFactory for persistence unit 'default'
2016-10-10 19:11:11.066  INFO 63856 --- [           main] org.hibernate.tool.hbm2ddl.SchemaExport  : HHH000227: Running hbm2ddl schema export
2016-10-10 19:11:11.068  INFO 63856 --- [           main] org.hibernate.tool.hbm2ddl.SchemaExport  : HHH000230: Schema export complete
2016-10-10 19:11:11.076  INFO 63856 --- [           main] utoConfigurationReportLoggingInitializer : 

Error starting ApplicationContext. To display the auto-configuration report re-run your application with 'debug' enabled.
2016-10-10 19:11:11.084 ERROR 63856 --- [           main] o.s.boot.SpringApplication               : Application startup failed

org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'cityService' defined in file [/Users/pwebb/projects/spring-boot/samples/spring-boot-issue-6871/target/classes/com/example/CityService.class]: Unsatisfied dependency expressed through constructor parameter 0; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'cityRepository': Post-processing of FactoryBean's singleton object failed; nested exception is org.mockito.exceptions.base.MockitoException: 
Cannot mock/spy class com.example.$Proxy81
Mockito cannot mock/spy following:
  - final classes
  - anonymous classes
  - primitive types
    at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:749) ~[spring-beans-4.3.3.RELEASE.jar:4.3.3.RELEASE]
    at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:189) ~[spring-beans-4.3.3.RELEASE.jar:4.3.3.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireConstructor(AbstractAutowireCapableBeanFactory.java:1148) ~[spring-beans-4.3.3.RELEASE.jar:4.3.3.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1051) ~[spring-beans-4.3.3.RELEASE.jar:4.3.3.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:510) ~[spring-beans-4.3.3.RELEASE.jar:4.3.3.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:482) ~[spring-beans-4.3.3.RELEASE.jar:4.3.3.RELEASE]
    at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:306) ~[spring-beans-4.3.3.RELEASE.jar:4.3.3.RELEASE]
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230) ~[spring-beans-4.3.3.RELEASE.jar:4.3.3.RELEASE]
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:302) ~[spring-beans-4.3.3.RELEASE.jar:4.3.3.RELEASE]
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197) ~[spring-beans-4.3.3.RELEASE.jar:4.3.3.RELEASE]
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:751) ~[spring-beans-4.3.3.RELEASE.jar:4.3.3.RELEASE]
    at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:861) ~[spring-context-4.3.3.RELEASE.jar:4.3.3.RELEASE]
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:541) ~[spring-context-4.3.3.RELEASE.jar:4.3.3.RELEASE]
    at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:761) ~[classes/:na]
    at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:371) ~[classes/:na]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:315) ~[classes/:na]
    at org.springframework.boot.test.context.SpringBootContextLoader.loadContext(SpringBootContextLoader.java:111) [classes/:na]
    at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContextInternal(DefaultCacheAwareContextLoaderDelegate.java:98) [spring-test-4.3.3.RELEASE.jar:4.3.3.RELEASE]
    at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:116) [spring-test-4.3.3.RELEASE.jar:4.3.3.RELEASE]
    at org.springframework.test.context.support.DefaultTestContext.getApplicationContext(DefaultTestContext.java:83) [spring-test-4.3.3.RELEASE.jar:4.3.3.RELEASE]
    at org.springframework.test.context.web.ServletTestExecutionListener.setUpRequestContextIfNecessary(ServletTestExecutionListener.java:189) [spring-test-4.3.3.RELEASE.jar:4.3.3.RELEASE]
    at org.springframework.test.context.web.ServletTestExecutionListener.prepareTestInstance(ServletTestExecutionListener.java:131) [spring-test-4.3.3.RELEASE.jar:4.3.3.RELEASE]
    at org.springframework.test.context.TestContextManager.prepareTestInstance(TestContextManager.java:230) [spring-test-4.3.3.RELEASE.jar:4.3.3.RELEASE]
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.createTest(SpringJUnit4ClassRunner.java:228) [spring-test-4.3.3.RELEASE.jar:4.3.3.RELEASE]
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner$1.runReflectiveCall(SpringJUnit4ClassRunner.java:287) [spring-test-4.3.3.RELEASE.jar:4.3.3.RELEASE]
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12) [junit-4.12.jar:4.12]
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.methodBlock(SpringJUnit4ClassRunner.java:289) [spring-test-4.3.3.RELEASE.jar:4.3.3.RELEASE]
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:247) [spring-test-4.3.3.RELEASE.jar:4.3.3.RELEASE]
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:94) [spring-test-4.3.3.RELEASE.jar:4.3.3.RELEASE]
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290) [junit-4.12.jar:4.12]
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71) [junit-4.12.jar:4.12]
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288) [junit-4.12.jar:4.12]
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58) [junit-4.12.jar:4.12]
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268) [junit-4.12.jar:4.12]
    at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61) [spring-test-4.3.3.RELEASE.jar:4.3.3.RELEASE]
    at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70) [spring-test-4.3.3.RELEASE.jar:4.3.3.RELEASE]
    at org.junit.runners.ParentRunner.run(ParentRunner.java:363) [junit-4.12.jar:4.12]
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:191) [spring-test-4.3.3.RELEASE.jar:4.3.3.RELEASE]
    at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:86) [.cp/:na]
    at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38) [.cp/:na]
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:459) [.cp/:na]
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:675) [.cp/:na]
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:382) [.cp/:na]
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:192) [.cp/:na]
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'cityRepository': Post-processing of FactoryBean's singleton object failed; nested exception is org.mockito.exceptions.base.MockitoException: 
Cannot mock/spy class com.example.$Proxy81
Mockito cannot mock/spy following:
  - final classes
  - anonymous classes
  - primitive types
    at org.springframework.beans.factory.support.FactoryBeanRegistrySupport.getObjectFromFactoryBean(FactoryBeanRegistrySupport.java:116) ~[spring-beans-4.3.3.RELEASE.jar:4.3.3.RELEASE]
    at org.springframework.beans.factory.support.AbstractBeanFactory.getObjectForBeanInstance(AbstractBeanFactory.java:1600) ~[spring-beans-4.3.3.RELEASE.jar:4.3.3.RELEASE]
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:317) ~[spring-beans-4.3.3.RELEASE.jar:4.3.3.RELEASE]
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202) ~[spring-beans-4.3.3.RELEASE.jar:4.3.3.RELEASE]
    at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:207) ~[spring-beans-4.3.3.RELEASE.jar:4.3.3.RELEASE]
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1128) ~[spring-beans-4.3.3.RELEASE.jar:4.3.3.RELEASE]
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1056) ~[spring-beans-4.3.3.RELEASE.jar:4.3.3.RELEASE]
    at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:835) ~[spring-beans-4.3.3.RELEASE.jar:4.3.3.RELEASE]
    at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:741) ~[spring-beans-4.3.3.RELEASE.jar:4.3.3.RELEASE]
    ... 43 common frames omitted
Caused by: org.mockito.exceptions.base.MockitoException: 
Cannot mock/spy class com.example.$Proxy81
Mockito cannot mock/spy following:
  - final classes
  - anonymous classes
  - primitive types
    at org.springframework.boot.test.mock.mockito.SpyDefinition.createSpy(SpyDefinition.java:99) ~[classes/:na]
    at org.springframework.boot.test.mock.mockito.MockitoPostProcessor.createSpyIfNecessary(MockitoPostProcessor.java:332) ~[classes/:na]
    at org.springframework.boot.test.mock.mockito.MockitoPostProcessor$SpyPostProcessor.createSpyIfNecessary(MockitoPostProcessor.java:490) ~[classes/:na]
    at org.springframework.boot.test.mock.mockito.MockitoPostProcessor$SpyPostProcessor.postProcessAfterInitialization(MockitoPostProcessor.java:486) ~[classes/:na]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyBeanPostProcessorsAfterInitialization(AbstractAutowireCapableBeanFactory.java:422) ~[spring-beans-4.3.3.RELEASE.jar:4.3.3.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.postProcessObjectFromFactoryBean(AbstractAutowireCapableBeanFactory.java:1728) ~[spring-beans-4.3.3.RELEASE.jar:4.3.3.RELEASE]
    at org.springframework.beans.factory.support.FactoryBeanRegistrySupport.getObjectFromFactoryBean(FactoryBeanRegistrySupport.java:113) ~[spring-beans-4.3.3.RELEASE.jar:4.3.3.RELEASE]
    ... 51 common frames omitted

2016-10-10 19:11:11.086 ERROR 63856 --- [           main] o.s.test.context.TestContextManager      : Caught exception while allowing TestExecutionListener [org.springframework.test.context.web.ServletTestExecutionListener@7dc222ae] to prepare test instance [com.example.SpringBootIssueApplicationTests@794eeaf8]

java.lang.IllegalStateException: Failed to load ApplicationContext
    at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:124) ~[spring-test-4.3.3.RELEASE.jar:4.3.3.RELEASE]
    at org.springframework.test.context.support.DefaultTestContext.getApplicationContext(DefaultTestContext.java:83) ~[spring-test-4.3.3.RELEASE.jar:4.3.3.RELEASE]
    at org.springframework.test.context.web.ServletTestExecutionListener.setUpRequestContextIfNecessary(ServletTestExecutionListener.java:189) ~[spring-test-4.3.3.RELEASE.jar:4.3.3.RELEASE]
    at org.springframework.test.context.web.ServletTestExecutionListener.prepareTestInstance(ServletTestExecutionListener.java:131) ~[spring-test-4.3.3.RELEASE.jar:4.3.3.RELEASE]
    at org.springframework.test.context.TestContextManager.prepareTestInstance(TestContextManager.java:230) ~[spring-test-4.3.3.RELEASE.jar:4.3.3.RELEASE]
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.createTest(SpringJUnit4ClassRunner.java:228) [spring-test-4.3.3.RELEASE.jar:4.3.3.RELEASE]
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner$1.runReflectiveCall(SpringJUnit4ClassRunner.java:287) [spring-test-4.3.3.RELEASE.jar:4.3.3.RELEASE]
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12) [junit-4.12.jar:4.12]
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.methodBlock(SpringJUnit4ClassRunner.java:289) [spring-test-4.3.3.RELEASE.jar:4.3.3.RELEASE]
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:247) [spring-test-4.3.3.RELEASE.jar:4.3.3.RELEASE]
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:94) [spring-test-4.3.3.RELEASE.jar:4.3.3.RELEASE]
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290) [junit-4.12.jar:4.12]
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71) [junit-4.12.jar:4.12]
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288) [junit-4.12.jar:4.12]
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58) [junit-4.12.jar:4.12]
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268) [junit-4.12.jar:4.12]
    at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61) [spring-test-4.3.3.RELEASE.jar:4.3.3.RELEASE]
    at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70) [spring-test-4.3.3.RELEASE.jar:4.3.3.RELEASE]
    at org.junit.runners.ParentRunner.run(ParentRunner.java:363) [junit-4.12.jar:4.12]
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:191) [spring-test-4.3.3.RELEASE.jar:4.3.3.RELEASE]
    at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:86) [.cp/:na]
    at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38) [.cp/:na]
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:459) [.cp/:na]
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:675) [.cp/:na]
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:382) [.cp/:na]
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:192) [.cp/:na]
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'cityService' defined in file [/Users/pwebb/projects/spring-boot/samples/spring-boot-issue-6871/target/classes/com/example/CityService.class]: Unsatisfied dependency expressed through constructor parameter 0; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'cityRepository': Post-processing of FactoryBean's singleton object failed; nested exception is org.mockito.exceptions.base.MockitoException: 
Cannot mock/spy class com.example.$Proxy81
Mockito cannot mock/spy following:
  - final classes
  - anonymous classes
  - primitive types
    at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:749) ~[spring-beans-4.3.3.RELEASE.jar:4.3.3.RELEASE]
    at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:189) ~[spring-beans-4.3.3.RELEASE.jar:4.3.3.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireConstructor(AbstractAutowireCapableBeanFactory.java:1148) ~[spring-beans-4.3.3.RELEASE.jar:4.3.3.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1051) ~[spring-beans-4.3.3.RELEASE.jar:4.3.3.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:510) ~[spring-beans-4.3.3.RELEASE.jar:4.3.3.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:482) ~[spring-beans-4.3.3.RELEASE.jar:4.3.3.RELEASE]
    at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:306) ~[spring-beans-4.3.3.RELEASE.jar:4.3.3.RELEASE]
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230) ~[spring-beans-4.3.3.RELEASE.jar:4.3.3.RELEASE]
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:302) ~[spring-beans-4.3.3.RELEASE.jar:4.3.3.RELEASE]
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197) ~[spring-beans-4.3.3.RELEASE.jar:4.3.3.RELEASE]
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:751) ~[spring-beans-4.3.3.RELEASE.jar:4.3.3.RELEASE]
    at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:861) ~[spring-context-4.3.3.RELEASE.jar:4.3.3.RELEASE]
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:541) ~[spring-context-4.3.3.RELEASE.jar:4.3.3.RELEASE]
    at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:761) ~[classes/:na]
    at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:371) ~[classes/:na]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:315) ~[classes/:na]
    at org.springframework.boot.test.context.SpringBootContextLoader.loadContext(SpringBootContextLoader.java:111) ~[classes/:na]
    at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContextInternal(DefaultCacheAwareContextLoaderDelegate.java:98) ~[spring-test-4.3.3.RELEASE.jar:4.3.3.RELEASE]
    at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:116) ~[spring-test-4.3.3.RELEASE.jar:4.3.3.RELEASE]
    ... 25 common frames omitted
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'cityRepository': Post-processing of FactoryBean's singleton object failed; nested exception is org.mockito.exceptions.base.MockitoException: 
Cannot mock/spy class com.example.$Proxy81
Mockito cannot mock/spy following:
  - final classes
  - anonymous classes
  - primitive types
    at org.springframework.beans.factory.support.FactoryBeanRegistrySupport.getObjectFromFactoryBean(FactoryBeanRegistrySupport.java:116) ~[spring-beans-4.3.3.RELEASE.jar:4.3.3.RELEASE]
    at org.springframework.beans.factory.support.AbstractBeanFactory.getObjectForBeanInstance(AbstractBeanFactory.java:1600) ~[spring-beans-4.3.3.RELEASE.jar:4.3.3.RELEASE]
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:317) ~[spring-beans-4.3.3.RELEASE.jar:4.3.3.RELEASE]
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202) ~[spring-beans-4.3.3.RELEASE.jar:4.3.3.RELEASE]
    at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:207) ~[spring-beans-4.3.3.RELEASE.jar:4.3.3.RELEASE]
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1128) ~[spring-beans-4.3.3.RELEASE.jar:4.3.3.RELEASE]
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1056) ~[spring-beans-4.3.3.RELEASE.jar:4.3.3.RELEASE]
    at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:835) ~[spring-beans-4.3.3.RELEASE.jar:4.3.3.RELEASE]
    at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:741) ~[spring-beans-4.3.3.RELEASE.jar:4.3.3.RELEASE]
    ... 43 common frames omitted
Caused by: org.mockito.exceptions.base.MockitoException: 
Cannot mock/spy class com.example.$Proxy81
Mockito cannot mock/spy following:
  - final classes
  - anonymous classes
  - primitive types
    at org.springframework.boot.test.mock.mockito.SpyDefinition.createSpy(SpyDefinition.java:99) ~[classes/:na]
    at org.springframework.boot.test.mock.mockito.MockitoPostProcessor.createSpyIfNecessary(MockitoPostProcessor.java:332) ~[classes/:na]
    at org.springframework.boot.test.mock.mockito.MockitoPostProcessor$SpyPostProcessor.createSpyIfNecessary(MockitoPostProcessor.java:490) ~[classes/:na]
    at org.springframework.boot.test.mock.mockito.MockitoPostProcessor$SpyPostProcessor.postProcessAfterInitialization(MockitoPostProcessor.java:486) ~[classes/:na]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyBeanPostProcessorsAfterInitialization(AbstractAutowireCapableBeanFactory.java:422) ~[spring-beans-4.3.3.RELEASE.jar:4.3.3.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.postProcessObjectFromFactoryBean(AbstractAutowireCapableBeanFactory.java:1728) ~[spring-beans-4.3.3.RELEASE.jar:4.3.3.RELEASE]
    at org.springframework.beans.factory.support.FactoryBeanRegistrySupport.getObjectFromFactoryBean(FactoryBeanRegistrySupport.java:113) ~[spring-beans-4.3.3.RELEASE.jar:4.3.3.RELEASE]
    ... 51 common frames omitted

@philwebb philwebb added type: bug A general bug and removed status: waiting-for-triage An issue we've not yet triaged labels Oct 11, 2016
@philwebb philwebb added this to the 1.4.2 milestone Oct 11, 2016
@philwebb
Copy link
Member

@igormukhin is there any particular reason why you need to use @SpyBean? Obviously we'd like to fix this, but I'm not sure how easy it will be. You might be better to either try @MockBean or switching to a test that actually uses the database result, rather than a spy.

@igormukhin
Copy link
Author

@philwebb I stunble upon it as I worked on an integration test where I wanted to check if a specific save operation is called at the end. But still I needed that all other (find... methods) to work as usual.

As a workaround I'm checking if the saved entry is in the database.

@ianaz
Copy link

ianaz commented Oct 25, 2016

+1. Same reason here

@micke239
Copy link

micke239 commented Nov 3, 2016

+1. Same issue for the same reason (wanting to verify interactions). Using spring-data-mongo, though.

@snicoll snicoll modified the milestones: 1.4.2, 1.4.3 Nov 8, 2016
@hashpyrit
Copy link

hashpyrit commented Nov 16, 2016

I encountered this problem as well. I got around it by creating my mock as follows:

MyClass spyOfSpringProxy = Mockito.mock(MyClass.class, AdditionalAnswers.delegatesTo(springCreatedProxy));

AdditionalAnswers.deletegateTo() works because the object you are delegating to does NOT have to be the same type as the type of the mock. I was able to use it in the same manner as a regular Mockito spy and was able to verify interactions.

@odrotbohm
Copy link
Member

odrotbohm commented Dec 19, 2016

When wanting to test interactions isn't a plain unit test of the client with a mock of the repository sufficient? It wouldn't even need to be an integration then would it?

It feels like you're mixing up two aspects of testing here: integration tests that check the behavior end to end and the desire to verify on internals of the tested component. I'd argue that's sort of violating the SRP principle in tests (if there is such thing in tests). Either test the thing as whole, then it's inputs against output. Or a dedicated component whose interaction with collaborators you inspect in detail.

@hashpyrit
Copy link

My specific use-case was verifying that database transactions were being rolled back as expected if a call to the repository (made in a service method) failed. Transaction was being applied as the service level. Hence I wanted to make the repository throw an exception. I wanted to use the functionality real repository most of the time except for the failure case. A spy was appropriate here. The component under test was the service but due to the nature of what it was doing, simply mocking the repository would not have been as complete a test as using a real repository.

@odrotbohm
Copy link
Member

So were you testing that an exception within a transaction rolls back the transaction? Then you're basically testing Spring Frameworks transaction implementation, don't you?

@hashpyrit
Copy link

hashpyrit commented Dec 19, 2016

I'm making sure I am using Spring transactions properly. I chose to use the @Transactional annotation to do this. It is conceivable that someone could remove that annotation not knowing that removing the annotation would prevent the transaction from being applied, and thus my service method would not function as it should. This why I wanted to test this. I'm not testing Spring transactions per se, but my usage of Spring transactions.

@odrotbohm
Copy link
Member

Gotcha, good point! 👍 I guess switching to a mock would then break the other tests that really want to persist data, right?

@hashpyrit
Copy link

hashpyrit commented Dec 19, 2016

Honestly like 99% of the time I use mocks for my dependencies (e.g. repositories) when making tests for my services. And most of the time my service methods aren't making multiple calls to a repository methods so there really is not much point in making the service method transactional. It was just this case of using the @Transactional annotation that I wanted to make sure the transaction was rolled back in case of an error thrown by the repository. By spying on the repository I was able to make calls to the repo, force a failure and then check after that the changes were NOT committed (because of the rollback). I previously tried using a mock repository and hooking into the Spring Transaction mechanism to listen for "rollback events" but I could not get it to work.

@hashpyrit
Copy link

At the end of the day, it's not too often people have to use spies but there are cases when it's the option that gets the job done.

@philwebb philwebb modified the milestones: 1.4.4, 1.4.3 Dec 20, 2016
@robertotru
Copy link

robertotru commented Dec 22, 2016

Mine is just a guess, but maybe using Mockito's @Spy on the bean created via @Resource and then injected by using @InjectMock should act as as a replacement of the @SpyBean annotation.
Am I wrong?

@hashpyrit
Copy link

hashpyrit commented Dec 22, 2016

I assume you mean the @InjectMocks annotation provided by Mockito? In my case I am using the SpringRunner Junit runner. I don't think @InjectMocks would work with that because I thought you needed to use the MockitoJunitRunner for annotations like @Mock and @InjectMocks to work.

@robertotru
Copy link

robertotru commented Dec 22, 2016

Yeah, right! I made a test and I could only make it working by annotating the autowired beans with @Spy and then setting the spied beans in the tested component using ReflectionTestUtils.setField. I wonder if there is out of there a runner delegator as we have one for PowerMock, so that the main runner will be MockitoJUnitRunner and the delegated will be SpringRunner.

@kuhnroyal
Copy link

I followed @hashpyrit's advice, using a special configuration for tests. It works well and is completely decoupled from the test cases.

@Configuration
public class MockRepositoryConfiguration {

    @Primary
    @Bean(name = "fooRepositoryMock")
    FooRepository fooRepository(final FooRepository real) {
        // workaround for https://github.com/spring-projects/spring-boot/issues/7033
        return Mockito.mock(FooRepository.class, AdditionalAnswers.delegatesTo(real));
    }
}

@philwebb philwebb modified the milestones: 2.0.0, 1.4.4 Jan 18, 2017
@philwebb philwebb modified the milestones: 2.5.x, 2.6.x Mar 19, 2021
@spc16670
Copy link

This issue has been chasing milestones for 5 years now...

I have seen that some specific jdk+mockito+spring version combinations appear to somehow work while other throw the UnsatisfiedDependencyExceptions.

If the issue is not going to be fixed anytime soon, could we perhaps publish a list of jdk+mockito+spring version combinations that are known to work?

I can confirm that:

openjdk version "11.0.11-ea" 2021-04-20 + mockito-core 3.6.0 + spring-test 5.3.1 from spring boot 2.4.0 does need a workaround mentioned by @onacit

@spyro2000
Copy link

spybean wasn't working for me once, just seems to be null.

@wilkinsona wilkinsona changed the title @SpyBean on Data Jpa Repository bean isn't working @SpyBean does not work when used to spy on a Spring Data Repository Jul 15, 2021
@wilkinsona wilkinsona self-assigned this Jul 15, 2021
@wilkinsona wilkinsona modified the milestones: 2.6.x, 2.5.x Jul 15, 2021
@wilkinsona
Copy link
Member

We have a fix for this. It's a little bit risky, but not too bad. As such, we've decided that 2.5.x is the best place for it.

@eiswind
Copy link

eiswind commented Jul 23, 2021

@wilkinsona thx for the fix after all this time. seems to work on my side.

@wilkinsona
Copy link
Member

That’s a relief. Thanks for letting us know and for your patience.

@mikelhamer

This comment was marked as off-topic.

@nightswimmings
Copy link

nightswimmings commented May 28, 2022

Im in 2.6.4/2.7.0 and

@SpyBean
TestEntityRepository testEntityRepository;

does not get reset across my tests when context is reused. Is easy to spot it because if you do a Mock.reset(testEntityRepository); before using it in the next test class that injects an Spy, everything goes green.

I double checked and ResetMocksTestExecutionListener is being executed after my first test

Is this still expected (i've read there still were some side-effects like this besides the main bug in previous comments)? @wilkinsona

BTW, nice session yesterday @snicoll on the Spring.io ! You caught me staring at you while seeking my mates, from the second floor of the staircase, I felt quite creepy xD

P.S: the bean does show up on ResetMocksTestExecutionListener.resetMocks, but MockReset.get(bean) does not return AFTER, when it should, it returns NONE because SpringBootMockResolver does not find the target object . This does not happen with my other regular service @SpyBean. Ill try to attach a minimum code that reproduces it as soon as possible if you tell me it is unexpected

@wilkinsona
Copy link
Member

@nightswimmings This issue was about the creation of the mock or spy, not resetting it. If reseting of a spied Spring Data repository isn't working for you, please open a new issue with a minimal example and we can take a look.

@nightswimmings
Copy link

Oks! If it is indeed unexpected, then I'll create the sample on a new issue, thanks!

@Lucasark

This comment was marked as off-topic.

@wilkinsona

This comment was marked as resolved.

@pavlo-reshetniak
Copy link

pavlo-reshetniak commented Apr 8, 2024

Looks like the issue is back. For me, the @SpyBean stopped working for Mongo repositories since the spring-boot-starter-parent:3.2.0 version. And everything was fine with the previous version: 3.1.10. I tried to follow the release notes, etc..., but don't see any breaking changes that may cause it.

The use case is trivial. When I'm doing in test:

@SpyBean
private MyFancyRepository<MyFancyType> myFancyRepository;

The exception occurs:

Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [com.xxx.repository.MyFancyRepository]: Specified class is an interface
	at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:77)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateBean(AbstractAutowireCapableBeanFactory.java:1311)
	... 39 more

And flipping the version back to 3.1.10 just fixes everything.
Should I file a bug for it? :)

@wilkinsona
Copy link
Member

@pavlo-reshetniak I'm not sure you have the same problem as the only occurrence of "Specified class is an interface" in this issue is in your comment. If you have a minimal reproducer that you can share, please open a new issue and we'll take a look.

@pavlo-reshetniak
Copy link

@wilkinsona, thank you for your reply.

I investigated a bit more and you are right, it's not related to the current thread. The issue is related to the generic type of the MongoRepository bean when initializing it via the @SpyBean.
I've submitted a separate ticket with the code example: #40234

Regards,
Pavlo

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
type: bug A general bug
Projects
None yet
Development

Successfully merging a pull request may close this issue.