5

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

Adriano
  • 19,463
  • 19
  • 103
  • 140

1 Answers1

11

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?

Community
  • 1
  • 1
Boris Treukhov
  • 17,493
  • 9
  • 70
  • 91
  • 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. – Adriano Oct 26 '12 at 21:11
  • @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 Oct 26 '12 at 21:14
  • @AdrienBe are you actually running this project as a test with jUnit 4+? Do you have any error messages in the console? – Boris Treukhov Oct 26 '12 at 21:26
  • I'm actually using TestNg. I updated my answer with the test-related imports – Adriano Oct 26 '12 at 21:31
  • 1
    http://stackoverflow.com/questions/2608528/spring-dependency-injection-with-testng – Boris Treukhov Oct 26 '12 at 21:32
  • 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. – Asturio Jul 13 '16 at 14:54