Announcement Announcement Module
Collapse
No announcement yet.
Issue Injecting Transactional DAO Object into Unit Test Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • Issue Injecting Transactional DAO Object into Unit Test

    Hi,

    I have a unit test setup which gets several resources @Autowired into it.

    One of them is a CmeInstrumentDao (interface of type Dao<CmeInstrument>). I have component auto-scanning enabled and have path to this CmeInstrumentDao configured properly. Everything works.

    Now I also have a HibernateCmeInstrumentDao which is specifically the Hibernate implementation. In my unit test I'm actually testing the Hibernate version of the Dao so I changed my @Autowired value from

    Code:
    @Autowired
    CmeInstrumentDao dao;
    to
    Code:
    @Autowired
    HibernateCmeInstrumentDao dao
    so I may access methods on the implementation which are missing from the interface.

    Both Dao's are in the same package and I checked my logs which state that this Hibernate...Dao is found. Yet when my test comes up to run I get an exception stating:

    Code:
    g.springframework.beans.factory.BeanCreationException: Error creating bean with name 'com.icarus.common.instuments.dao.HibernateCmeInstrumentDaoTest': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: private com.icarus.common.instruments.dao.HibernateCmeInstrumentDao com.icarus.common.instuments.dao.HibernateCmeInstrumentDaoTest.dao; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No matching bean of type [com.icarus.common.instruments.dao.HibernateCmeInstrumentDao] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}
    My unit test is setup as such:
    Code:
    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration
    @TransactionConfiguration
    public class HibernateCmeInstrumentDaoTest {
    
    	@Autowired
    	private SessionFactory sessionFactory;
    	@Autowired
    	private HibernateCmeInstrumentDao dao;
            // tests here
    }
    The *-context.xml file for the test does all the scanning (logs confirm scanning worked, beans are found) and also sets up transaction handling (I think this is causing my issues).

    (I'm posting Spring TRACE logs in a reply because they're too long otherwise)

    Any ideas why it suddenly can't find the bean? I know several other beans autowire with it and it doesn't seem to cause any issues. Perhaps this has something to do with having transactions enabled?

    Thank you for any help.

  • #2
    Here are some of the TRACE logs from spring:
    Code:
    2010-04-14 14:27:48,239 TRACE ClassPathBeanDefinitionScanner:209 - Scanning file [/home/mchrosto/workspace/maven.1257964511765/icarus-common/target/classes/com/icarus/common/instruments/dao/HibernateCmeInstrumentDao.class]
    2010-04-14 14:27:48,242 DEBUG ClassPathBeanDefinitionScanner:220 - Identified candidate component class: file [/home/mchrosto/workspace/maven.1257964511765/icarus-common/target/classes/com/icarus/common/instruments/dao/HibernateCmeInstrumentDao.class]
    ...
    2010-04-14 14:27:48,826 DEBUG GenericApplicationContext:468 - Bean factory for [email protected]1a408: org.springframework.beans.factory.support.DefaultListableBeanFactory@197200b4: defining beans [org.springframework.aop.config.internalAutoProxyCreator,org.springframework.transaction.annotation.AnnotationTransactionAttributeSource#0,org.springframework.transaction.interceptor.TransactionInterceptor#0,org.springframework.transaction.config.internalTransactionAdvisor,dataSource,sessionFactory,transactionManager,hibernateCurrencyDao,hibernateCmeInstrumentDao,hibernateFxInstrumentDao,hibernateFxTickDao,simpleCmeInstrumentFactory,cachedCmeInstrumentFactory,org.springframework.context.annotation.internalConfigurationAnnotationProcessor,org.springframework.context.annotation.internalAutowiredAnnotationProcessor,org.springframework.context.annotation.internalRequiredAnnotationProcessor,org.springframework.context.annotation.internalCommonAnnotationProcessor,org.springframework.context.annotation.internalPersistenceAnnotationProcessor,hibernateFuturesProductDao,hibernateFuturesContractDao,futuresProductFactory,outrightFuturesFactory,tradableStrategyFactory,hibernateTradableDao,hibernateTradableStrategyDao,hibernateTradableStrategyOptionContractDao,hibernateFuturesOptionStrategyDao,hibernateFuturesOptionDao,futuresOptionContractFactory,tradableStrategyOptionContractFactory,stockOptionFactory,hibernateStockOptionTickDao,hibernateStockOptionDao,hibernateStockTickDao,hibernateStockDao,stockFactory,org.springframework.beans.factory.config.PropertyOverrideConfigurer#0]; root of factory hierarchy
    ...
    2010-04-14 14:28:01,033 DEBUG DefaultListableBeanFactory:214 - Creating shared instance of singleton bean 'hibernateCmeInstrumentDao'
    2010-04-14 14:28:01,034 DEBUG DefaultListableBeanFactory:430 - Creating instance of bean 'hibernateCmeInstrumentDao'
    2010-04-14 14:28:01,036 DEBUG DefaultListableBeanFactory:242 - Returning cached instance of singleton bean 'sessionFactory'
    2010-04-14 14:28:01,036 DEBUG DefaultListableBeanFactory:724 - Autowiring by type from bean name 'hibernateCmeInstrumentDao' via constructor to bean named 'sessionFactory'
    2010-04-14 14:28:01,056 DEBUG DefaultListableBeanFactory:504 - Eagerly caching bean 'hibernateCmeInstrumentDao' to allow for resolving potential circular references
    2010-04-14 14:28:01,057 TRACE CachedIntrospectionResults:222 - Getting BeanInfo for class [com.icarus.common.instruments.dao.HibernateCmeInstrumentDao]
    2010-04-14 14:28:01,061 TRACE CachedIntrospectionResults:238 - Caching PropertyDescriptors for class [com.icarus.common.instruments.dao.HibernateCmeInstrumentDao]
    2010-04-14 14:28:01,062 TRACE CachedIntrospectionResults:246 - Found bean property 'class' of type [java.lang.Class]
    2010-04-14 14:28:01,062 TRACE CachedIntrospectionResults:246 - Found bean property 'hibernateTemplate' of type [org.springframework.orm.hibernate3.HibernateTemplate]
    2010-04-14 14:28:01,062 TRACE CachedIntrospectionResults:246 - Found bean property 'observers' of type [java.util.List]
    2010-04-14 14:28:01,063 TRACE CachedIntrospectionResults:246 - Found bean property 'sessionFactory' of type [org.hibernate.SessionFactory]
    2010-04-14 14:28:01,063 TRACE CachedIntrospectionResults:246 - Found bean property 'type' of type [java.lang.Class]
    2010-04-14 14:28:01,064 DEBUG DefaultListableBeanFactory:1453 - Invoking afterPropertiesSet() on bean with name 'hibernateCmeInstrumentDao'
    2010-04-14 14:28:01,064 DEBUG DefaultListableBeanFactory:242 - Returning cached instance of singleton bean 'org.springframework.transaction.config.internalTransactionAdvisor'
    2010-04-14 14:28:01,064 DEBUG AnnotationTransactionAttributeSource:106 - Adding transactional method 'load' with attribute: PROPAGATION_REQUIRED,ISOLATION_DEFAULT,readOnly; ''
    2010-04-14 14:28:01,065 DEBUG InfrastructureAdvisorAutoProxyCreator:537 - Creating implicit proxy for bean 'hibernateCmeInstrumentDao' with 0 common interceptors and 1 specific interceptors
    2010-04-14 14:28:01,065 DEBUG JdkDynamicAopProxy:113 - Creating JDK dynamic proxy: target source is SingletonTargetSource for target object [[email protected]f32]
    2010-04-14 14:28:01,073 DEBUG DefaultListableBeanFactory:458 - Finished creating instance of bean 'hibernateCmeInstrumentDao'
    ...
    2010-04-14 14:28:04,481 DEBUG InjectionMetadata:59 - Found injected element on class [com.icarus.common.instuments.dao.HibernateCmeInstrumentDaoTest]: AutowiredFieldElement for private org.hibernate.SessionFactory com.icarus.common.instuments.dao.HibernateCmeInstrumentDaoTest.sessionFactory
    2010-04-14 14:28:04,482 DEBUG InjectionMetadata:59 - Found injected element on class [com.icarus.common.instuments.dao.HibernateCmeInstrumentDaoTest]: AutowiredFieldElement for private com.icarus.common.instruments.dao.HibernateCmeInstrumentDao com.icarus.common.instuments.dao.HibernateCmeInstrumentDaoTest.dao
    ...
    2010-04-14 14:28:04,505 DEBUG DefaultListableBeanFactory:242 - Returning cached instance of singleton bean 'sessionFactory'
    2010-04-14 14:28:04,505 DEBUG AutowiredAnnotationBeanPostProcessor:421 - Autowiring by type from bean name 'com.icarus.common.instuments.dao.HibernateCmeInstrumentDaoTest' to bean named 'sessionFactory'
    2010-04-14 14:28:04,506 DEBUG InjectionMetadata:82 - Processing injected method of bean 'com.icarus.common.instuments.dao.HibernateCmeInstrumentDaoTest': AutowiredFieldElement for private com.icarus.common.instruments.dao.HibernateCmeInstrumentDao com.icarus.common.instuments.dao.HibernateCmeInstrumentDaoTest.dao

    Comment


    • #3
      and finally, the actual exception:
      Code:
      2010-04-14 14:28:04,524 ERROR TestContextManager:336 - Caught exception while allowing TestExecutionListener [org.springframework.test.context.support.DependencyInjectionTestExecutionListener@73a34b] to prepare test instance [[email protected]2ba11b]
      org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'com.icarus.common.instuments.dao.HibernateCmeInstrumentDaoTest': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: private com.icarus.common.instruments.dao.HibernateCmeInstrumentDao com.icarus.common.instuments.dao.HibernateCmeInstrumentDaoTest.dao; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No matching bean of type [com.icarus.common.instruments.dao.HibernateCmeInstrumentDao] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}
      	at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:286)
      	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1064)
      	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireBeanProperties(AbstractAutowireCapableBeanFactory.java:374)
      	at org.springframework.test.context.support.DependencyInjectionTestExecutionListener.injectDependencies(DependencyInjectionTestExecutionListener.java:110)
      	at org.springframework.test.context.support.DependencyInjectionTestExecutionListener.prepareTestInstance(DependencyInjectionTestExecutionListener.java:75)
      	at org.springframework.test.context.TestContextManager.prepareTestInstance(TestContextManager.java:333)
      	at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.createTest(SpringJUnit4ClassRunner.java:220)
      	at org.springframework.test.context.junit4.SpringJUnit4ClassRunner$1.runReflectiveCall(SpringJUnit4ClassRunner.java:301)
      	at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15)
      	at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.methodBlock(SpringJUnit4ClassRunner.java:303)
      	at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:240)
      	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:44)
      	at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:180)
      	at org.junit.runners.ParentRunner.access$000(ParentRunner.java:41)
      	at org.junit.runners.ParentRunner$1.evaluate(ParentRunner.java:173)
      	at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:28)
      	at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
      	at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:31)
      	at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70)
      	at org.junit.runners.ParentRunner.run(ParentRunner.java:220)
      	at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:180)
      	at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:46)
      	at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
      	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467)
      	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683)
      	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390)
      	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)
      Caused by: org.springframework.beans.factory.BeanCreationException: Could not autowire field: private com.icarus.common.instruments.dao.HibernateCmeInstrumentDao com.icarus.common.instuments.dao.HibernateCmeInstrumentDaoTest.dao; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No matching bean of type [com.icarus.common.instruments.dao.HibernateCmeInstrumentDao] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}
      	at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:507)
      	at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:84)
      	at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:283)
      	... 26 more
      Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No matching bean of type [com.icarus.common.instruments.dao.HibernateCmeInstrumentDao] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}
      	at org.springframework.beans.factory.support.DefaultListableBeanFactory.raiseNoSuchBeanDefinitionException(DefaultListableBeanFactory.java:903)
      	at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:772)
      	at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:686)
      	at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:478)
      	... 28 more

      Comment


      • #4
        Also in the logs, there are several instances where the "hibernateCmeInstrumentDao" bean is returned successfully for other beans that have to be wired to it:

        Code:
        2010-04-14 14:28:01,250 DEBUG DefaultListableBeanFactory:242 - Returning cached instance of singleton bean 'hibernateCmeInstrumentDao'
        ...
        2010-04-14 14:28:01,564 DEBUG DefaultListableBeanFactory:242 - Returning cached instance of singleton bean 'hibernateCmeInstrumentDao'
        2010-04-14 14:28:01,564 DEBUG DefaultListableBeanFactory:724 - Autowiring by type from bean name 'cachedCmeInstrumentFactory' via constructor to bean named 'hibernateCmeInstrumentDao'
        ...
        2010-04-14 14:28:03,903 DEBUG DefaultListableBeanFactory:724 - Autowiring by type from bean name 'simpleCmeInstrumentFactory' via constructor to bean named 'hibernateCmeInstrumentDao'
        It isn't until Spring tries to wire my unit tests 'dao' reference to the 'hibernateCmeInstrumentDao' that the instrument dao is suddenly missing.

        Comment


        • #5
          Also,

          I tried just letting the CmeInstrumentDao get autowired and just casting the type to Hibernate... when I wanted to access the implementation specific functions but then I got a ClassCastException.

          Apparently the HibernateCmeInstrumentDao gets overwritten by a Proxy class, likely for transactions, and then the autowiring can't find it.

          I'm guessing this is because my HibernateCmeInstrumentDao is implementing an interface named CmeInstrumentDao. Spring AOP is using dynamic proxies and thus my Dao is only exposed by the CmeInstrumentDao interface and not the HibernateCmeInstrumentDao type. Does this sound possible?

          I'm guessing if so then a CGLIB proxy should work in this instance. eh?
          Last edited by soks86; Apr 14th, 2010, 03:51 PM.

          Comment


          • #6
            Was going to answer but you already figured it out.

            But why use cglib as it is heavier and adds an extra dependency and to just introduce cglib for testing is an overkill IMHO..

            Comment


            • #7
              I agree Marten, thank you for the advice.

              I reconfigured my test instead of switching to CGLIB.

              Now my test-context.xml file imports a @Configuration class which is where I define my HibernateCmeInstrumentDao @Bean and setup the implementation specific details there, before it gets proxied out. I had to change my tests a little but with a few setter/getter methods on the @Configuration and I have access to the original non-proxied bean without any hacks.

              Unfortunately now my roll backs don't seem to be working >.< I'll investigate and start a new thread if I get stumped again.

              Thanks, again.

              Comment


              • #8
                I have the exactly same problem as you had. I wonder do you have a proper solution yet?

                Comment


                • #9
                  I considered my solution to be "proper." That is, it seems to me the only way to work around the Spring generated proxies is to use Spring.

                  For example you could use an XML bean definition and call the setters you need to within that definition or you could use Java-based @Configuration and call whatever methods you want when you're constructing the bean.

                  Both seem to work and allow you to @Autowire based on the interface instead of the concrete class. Being that interfaces exist to be used in place of concrete classes that last point should not be an issue.

                  If that doesn't make sense, feel free to post more details and I can try to help... not sure how correct the method I chose was but I think it could be applied to most situations without too much mess.

                  Comment

                  Working...
                  X