Spring + TestNG integration tests, injecting DAO with annotations fails

13,239

If you create unit tests, Spring IoC functionality is unavailable(as it was intended by the framework designers), because you are testing your objects in isolation(i.e. you are mocking only minimal set of interfaces which are required for the test to complete). In this case you should inject your mock repository manually, for example in @Before test initialization method. The whole idea is that your classes only depend on interfaces, so basically Spring container evaluates which class to use as the interface implementation, but when you create a unit test you need to have a strict control of which interface methods were called(and have a minimal set of dependencies), that is why you perform the injection manually.

If you are doing integration testing, you should have a Spring IoC container instance up and running, for this to work you should use jUnit(assuming that you are using jUnit) specific test runner, as it described in the Spring documentation on testing.

So, returning to the question, you have what looks like a simple unit test to jUnit, and the Spring container is not used. So, if you are going to use Spring TestContext framework, you should have something like

   @RunWith(SpringJUnit4ClassRunner.class)
   @ContextConfiguration(locations={"/path-to-app-config.xml", "/your-test-specific-config.xml"})
   public class UserDaoImplHibernateTests

instead of @Component.

update in TestNg case I think it should be (I used Spring Dependency Injection with TestNG as the reference)

   @ContextConfiguration(locations={"/path-to-app-config.xml", "/your-test-specific-config.xml"})
   public class UserDaoImplHibernateTests extends AbstractTestNGSpringContextTests

See also: What is the difference between integration and unit tests?

Share:
13,239
Adrien Be
Author by

Adrien Be

Think twice.

Updated on June 10, 2022

Comments

  • Adrien Be
    Adrien Be almost 2 years

    I first did not mention what was the key component of this issue: I am using TestNG here.

    I have a DAO layer performing persistence. It works fine as part of my little web app (I have a classic Controller, Service, DAO layers design). I can update this question with my XMLs if required.

    My Service layer

    @Service
    public class UserServiceImpl implements UserService {
    
        @Autowired
        private UserDao userDao;
    
        @Override
        public GoodVibeUserDetails getUser(String username) throws UsernameNotFoundException {
    
            GoodVibeUserDetails user = userDao.getDetailsRolesAndImagesForUser(username);
    
            return user;
        }
    
        // more methods...
    
    }
    

    My DAO layer

    @Repository
    public class UserDaoImplHibernate implements UserDao {
    
        @Autowired
        private SessionFactory sessionFactory;
    
        // My methods using sessionFactory & "talking" to the Db via the sessionFactory
    }
    

    And here is my Test Class

    @Component
    public class UserDaoImplHibernateTests{
    
        @Autowired
        private UserDao userDao;
    
        private GoodVibeUserDetails user; 
    
        @BeforeMethod
        public void beforeEachMethod() throws ParseException{
            user = new GoodVibeUserDetails();
            user.setUsername("adrien");
            user.setActive(true);
            // & so on...
        }
    
        /*
         * When everything is fine - test cases
         */
        @Test
        public void shouldAcceptRegistrationAndReturnUserWithId() throws Exception{
            assertNotNull(userDao) ;
            user = userDao.registerUser(user);
            assertNotNull(user.getId()) ;
        }
    
        // more test cases...
    
    }
    

    But for my test class the Autowiring, userDao always returns Null, I'm only starting to do tests in Spring and I'm a bit lost. Any pointers are welcome.


    Latest edit after Boris Treukhov's answer

    import ...
    import org.junit.runner.RunWith;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.test.context.ContextConfiguration;
    import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
    import org.testng.annotations.BeforeMethod;
    import org.testng.annotations.Test;
    import static org.junit.Assert.assertNotNull;
    
    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration("/applicationContext.xml")
    public class UserDaoImplHibernateTests{
    
        @Autowired
        @Qualifier("userDao")
        private UserDao userDao;
    
        private GoodVibeUserDetails user; 
    
        @BeforeMethod
        public void beforeEachMethod() throws ParseException{
            user = new GoodVibeUserDetails();
            user.setUsername("adrien");
            user.setActive(true);
            // & so on...
        }
    
        /*
         * When everything is fine - test cases
         */
        @Test
        public void shouldAcceptRegistrationAndReturnUserWithId() throws Exception{
            assertNotNull(userDao) ;
            user = userDao.registerUser(user);
            assertNotNull(user.getId()) ;
        }
    
        // more test methods...
    
    }
    

    And this is my applicationContext.xml

    <?xml version="1.0" encoding="UTF-8"?>
    
    <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
        xmlns:tx="http://www.springframework.org/schema/tx" 
        xmlns:context="http://www.springframework.org/schema/context" 
        xmlns:p="http://www.springframework.org/schema/p" 
        xsi:schemaLocation="http://www.springframework.org/schema/beans 
        http://www.springframework.org/schema/beans/spring-beans-3.0.xsd 
        http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd 
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd" >
    
    
        <!-- the application context definition scans within the base package of the application -->
        <!-- for @Components, @Controller, @Service, @Configuration, etc. -->
        <context:annotation-config />
        <context:component-scan base-package="com.goodvibes" />
    
        <bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer" p:location="/WEB-INF/jdbc.properties" />
    
        <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close" 
            p:driverClassName="${jdbc.driverClassName}" p:url="${jdbc.databaseurl}" 
            p:username="${jdbc.username}" p:password="${jdbc.password}" />
    
    
        <bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
            <property name="dataSource" ref="dataSource" />
            <property name="configLocation">
                <value>classpath:hibernate.cfg.xml</value>
            </property>
            <property name="configurationClass">
                <value>org.hibernate.cfg.AnnotationConfiguration</value>
            </property>
            <property name="hibernateProperties">
                <props>
                    <prop key="hibernate.dialect">${jdbc.dialect}</prop>
                    <prop key="hibernate.show_sql">${jdbc.show_sql}</prop>
                    <prop key="hibernate.connection.SetBigStringTryClob">true</prop>
                    <prop key="hibernate.jdbc.batch_size">0</prop>
                </props>
            </property>
        </bean>
    
        <tx:annotation-driven />
    
        <bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
            <property name="sessionFactory" ref="sessionFactory" />
        </bean>
    
        [...]
    
    </beans>
    

    I did not add a repository-config.xml as this should be enough to access userDao. I still get userDao equal null though.

    Thanks in advance

  • Adrien Be
    Adrien Be over 11 years
    thanks Boris, you definitely pointed me into the right direction. I am still getting userDao equal null though. I updated my question with the changes I made following your answer. I am obviously missing something.
  • Boris Treukhov
    Boris Treukhov over 11 years
    @AdrienBe remove the @Qualifier("userDao") as you have not specified the name in @Repository annotation of UserDaoImplHibernate is suppose it will be userDaoImplHibernate (but I don't rememeber exactly :-), anyway using name qualifier is absolutely unnecessary here.
  • Boris Treukhov
    Boris Treukhov over 11 years
    @AdrienBe are you actually running this project as a test with jUnit 4+? Do you have any error messages in the console?
  • Adrien Be
    Adrien Be over 11 years
    I'm actually using TestNg. I updated my answer with the test-related imports
  • Boris Treukhov
    Boris Treukhov over 11 years
  • Adrien Be
    Adrien Be over 11 years
    Thanks a lot! you made my evening :)
  • Asturio
    Asturio almost 8 years
    OK, this is a little old, but there is also another problem here: The OP was mixing JUnit and TestNG in the same Test-Class. The Tests were annotated with TestNG, but the Runner and the Assert are JUnit. This just couldn't work. So, the Runner would setup the Spring Context, and the DAO should be autowired, but no Tests would be found.