Интеграционные тесты Spring + TestNG, не удается ввести DAO с аннотациями

Сначала я не упомянул, что было ключевым компонентом этой проблемы: здесь я использую TestNG.

У меня есть слой DAO, выполняющий постоянство. Он отлично работает как часть моего небольшого веб-приложения (у меня классический дизайн слоев Controller, Service, DAO). При необходимости я могу обновить этот вопрос, добавив свои XML-файлы.

Уровень My Service

@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...

}

Мой слой DAO

@Repository
public class UserDaoImplHibernate implements UserDao {

    @Autowired
    private SessionFactory sessionFactory;

    // My methods using sessionFactory & "talking" to the Db via the sessionFactory
}

А вот и мой тестовый класс

@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...

}

Но для моего тестового класса Autowiring userDao всегда возвращает Null, я только начинаю проводить тесты весной и немного растерялся. Любые указатели приветствуются.


Последнее изменение после ответа Бориса Треухова

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...

}

А это мой 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>

Я не добавил репозиторий-config.xml, так как этого должно быть достаточно для доступа к userDao. Я все еще получаю userDao равным null.

заранее спасибо


person Adrien Be    schedule 26.10.2012    source источник


Ответы (1)


Если вы создаете модульные тесты, функциональность Spring IoC недоступна (как это было задумано разработчиками фреймворка), потому что вы тестируете свои объекты изолированно (т. Е. Вы имитируете только минимальный набор интерфейсов, необходимых для завершения теста). В этом случае вам следует ввести свой фиктивный репозиторий вручную, например, в методе инициализации теста @Before. Вся идея заключается в том, что ваши классы зависят только от интерфейсов, поэтому в основном контейнер Spring оценивает, какой класс использовать в качестве реализации интерфейса, но когда вы создаете модульный тест, вам необходимо строго контролировать, какие методы интерфейса были вызваны (и иметь минимальный набор зависимостей), поэтому вы выполняете инъекцию вручную.

Если вы выполняете интеграционное тестирование, у вас должен быть запущен и запущен экземпляр контейнера Spring IoC, чтобы это работало, вы должны использовать jUnit (при условии, что вы используете jUnit) специальный тестовый исполнитель, как это описано в Документация Spring по тестированию.

Итак, возвращаясь к вопросу, у вас есть то, что выглядит как простой модульный тест для jUnit, а контейнер Spring не используется. Итак, если вы собираетесь использовать фреймворк Spring TestContext, у вас должно быть что-то вроде

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

вместо @Component.

update в случае TestNg, я думаю, это должно быть (я использовал Spring Dependency Injection с TestNG в качестве ссылки)

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

См. Также: В чем разница между интеграцией и модульными тестами? < / а>

person Boris Treukhov    schedule 26.10.2012
comment
Спасибо, Борис, ты определенно указал мне правильное направление. Я все еще получаю userDao равным null. Я обновил свой вопрос изменениями, внесенными после вашего ответа. Я явно что-то упускаю. - person Adrien Be; 27.10.2012
comment
@AdrienBe удалите @Qualifier("userDao"), поскольку вы не указали имя в @Repository аннотации UserDaoImplHibernate, предположим, что это будет userDaoImplHibernate (но я точно не помню :-), в любом случае использование квалификатора имени здесь совершенно не нужно. - person Boris Treukhov; 27.10.2012
comment
@AdrienBe, вы действительно запускаете этот проект в качестве теста с jUnit 4+? У вас есть сообщения об ошибках в консоли? - person Boris Treukhov; 27.10.2012
comment
На самом деле я использую TestNg. Я обновил свой ответ с помощью импорта, связанного с тестами - person Adrien Be; 27.10.2012
comment
stackoverflow.com/questions/2608528/ - person Boris Treukhov; 27.10.2012
comment
Большое спасибо! ты сделал мой вечер :) - person Adrien Be; 27.10.2012
comment
Хорошо, это немного старовато, но здесь есть еще одна проблема: OP смешивал JUnit и TestNG в одном и том же Test-Class. Тесты были аннотированы TestNG, но Runner и Assert - это JUnit. Это просто не могло сработать. Итак, Runner настроит Spring Context, а DAO должен быть автоматически подключен, но тесты не будут найдены. - person Asturio; 13.07.2016