Интеграционное тестирование в Spring: TestContext Framework, внедрение зависимостей тестовых приспособлений

Когда вы используете DependencyInjectionTestExecutionListener (который настроен по умолчанию), зависимости ваших тестовых экземпляров вводятся из bean-компонентов в контексте приложения, который вы настроили с помощью @ContextConfiguration или связанных аннотаций. Вы можете использовать внедрение сеттера, внедрение поля или и то, и другое, в зависимости от того, какие аннотации вы выбираете и размещаете ли вы их в методах или полях установщика. Если вы используете JUnit Jupiter, вы также можете дополнительно использовать внедрение конструктора (внедрение зависимостей с помощью SpringExtension). Для обеспечения согласованности с поддержкой Spring на основе аннотаций вы также можете использовать аннотацию Spring @Autowired или аннотацию @Inject из JSR-330 для внедрения полей и установщиков.

Для фреймворков тестирования, отличных от JUnit Jupiter, TestContext framework не участвует в создании экземпляра тестового класса. Таким образом, использование @Autowired или @Inject для конструкторов не влияет на тестовые классы.

Хотя в производственном коде внедрение в поле не поощряется, в тестовом коде внедрение в поле на самом деле вполне естественно. Разница объясняется тем, что вы никогда не создадите экземпляр своего тестового класса напрямую. Следовательно, нет необходимости вызывать общедоступный конструктор или метод установки в вашем тестовом классе.

Поскольку @Autowired используется для выполнения автоматического подключения по типу, если у вас есть несколько определений bean-компонентов одного типа, вы не можете полагаться на этот подход для этих конкретных bean-компонентов. В этом случае вы можете использовать @Autowired вместе с @Qualifier. Вы также можете использовать @Inject вместе с @Named. В качестве альтернативы, если ваш тестовый класс имеет доступ к своему ApplicationContext, вы можете выполнить явный поиск, используя (например) вызов applicationContext.getBean("titleRepository", TitleRepository.class).

Если вы не хотите, чтобы инъекция зависимостей применялась к вашим тестовым экземплярам, не аннотируйте поля или методы установки с помощью @Autowired или @Inject. Кроме того, вы можете полностью отключить внедрение зависимостей, явно настроив свой класс с помощью @TestExecutionListeners и исключив DependencyInjectionTestExecutionListener.class из списка слушателей.

Рассмотрим сценарий тестирования класса HibernateTitleRepository. Следующие два листинга кода демонстрируют использование @Autowired для полей и методов установки. Конфигурация контекста приложения представлена после всех примеров кода.

Поведение внедрения зависимостей в следующих листингах кода не относится к JUnit Jupiter. Те же методы DI можно использовать в сочетании с любым поддерживаемым фреймворком тестирования.

В следующих примерах выполняются вызовы статических методов утверждения, например assertNotNull(), но без добавления к вызову Assertions. В таких случаях предполагаем, что метод был правильно импортирован через статическое объявление импорта, которое не показано в примере.

В первом листинге кода показана реализация тестового класса на основе JUnit Jupiter, который использует @Autowired для внедрения поля:

@ExtendWith(SpringExtension.class)
// указывает конфигурацию Spring для загрузки 
// для этой тестовой фикстуры
@ContextConfiguration("repository-config.xml")
class HibernateTitleRepositoryTests {

    // этот экземпляр будет зависимостью, внедренной по типу
    @Autowired
    HibernateTitleRepository titleRepository;

    @Test
    void findById() {
        Title title = titleRepository.findById(new Long(10));
        assertNotNull(title);
    }
}

В качестве альтернативы вы можете настроить класс для использования @Autowired для внедрения установщика следующим образом:

@ExtendWith(SpringExtension.class)
// указывает конфигурацию Spring для загрузки 
// для этой тестовой фикстуры
@ContextConfiguration("repository-config.xml")
class HibernateTitleRepositoryTests {

    // этот экземпляр будет зависимостью, внедренной по типу
    HibernateTitleRepository titleRepository;

    @Autowired
    void setTitleRepository(HibernateTitleRepository titleRepository) {
        this.titleRepository = titleRepository;
    }

    @Test
    void findById() {
        Title title = titleRepository.findById(new Long(10));
        assertNotNull(title);
    }
}

В приведенных выше листингах кода используется тот же файл контекста XML, на который ссылается аннотация @ContextConfiguration (то есть repository-config.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"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd">

    <!-- этот bean-компонент будет внедрен в класс HibernateTitleRepositoryTests -->
    <bean id="titleRepository" class="com.foo.repository.hibernate.HibernateTitleRepository">
        <property name="sessionFactory" ref="sessionFactory"/>
    </bean>

    <bean id="sessionFactory" class="org.springframework.orm.hibernate5.LocalSessionFactoryBean">
        <!-- конфигурация опущена для краткости -->
    </bean>

</beans>

Если вы расширяетесь из предоставленного Spring базового класса тестирования, который использует @Autowired в одном из своих методов установки, у вас может быть несколько bean-компонентов затронутого типа, определенных в контексте вашего приложения (например, несколько bean-компонентов DataSource). В таком случае вы можете переопределить метод установки и использовать аннотацию @Qualifier, чтобы указать конкретный целевой bean-компонент, как показано ниже (но обязательно делегируйте переопределенный метод в суперклассе):

// ...

    @Autowired
    @Override
    public void setDataSource(@Qualifier("myDataSource") DataSource dataSource) {
        super.setDataSource(dataSource);
    }

// ...

Указанное значение квалификатора указывает конкретный bean-компонент DataSource для внедрения, сужая набор соответствий типов до конкретного bean-компонента. Его значение сопоставляется с объявлениями <qualifier> в соответствующих определениях <bean>. Имя bean-компонента используется в качестве значения квалификатора, поэтому вы также можете эффективно указать на конкретный bean-компонент по имени (как показано ранее, предполагая, что myDataSource является идентификатором bean-компонента).


Читайте также:


Комментарии

Популярные сообщения из этого блога

Методы класса Object в Java

Как получить текущий timestamp в Java

Основные опции JVM для повышения производительности и отладки