Spring тестовая инъекция не работает при использовании TestExecutionListener

Я хочу использовать пользовательскую TestExecutionListener в сочетании с SpringJUnit4ClassRunner для запуска настройки схемы Liquibase в моей тестовой базе данных. Мой TestExecutionListener работает нормально, но когда я использую аннотацию в своем классе, инъекция тестируемого DAO больше не работает, по крайней мере, экземпляр равен нулю.

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "file:src/main/webapp/WEB-INF/applicationContext-test.xml" })
@TestExecutionListeners({ LiquibaseTestExecutionListener.class })
@LiquibaseChangeSet(changeSetLocations={"liquibase/v001/createTables.xml"})
public class DeviceDAOTest {

    ...

    @Inject
    DeviceDAO deviceDAO;

    @Test
    public void findByCategory_categoryHasSubCategories_returnsAllDescendantsDevices() {
        List<Device> devices = deviceDAO.findByCategory(1); // deviceDAO null -> NPE
        ...
    }
}

Слушатель довольно прост:

public class LiquibaseTestExecutionListener extends AbstractTestExecutionListener {

    @Override
    public void beforeTestClass(TestContext testContext) throws Exception {
        final LiquibaseChangeSet annotation = AnnotationUtils.findAnnotation(testContext.getTestClass(),
                LiquibaseChangeSet.class);
        if (annotation != null) {
            executeChangesets(testContext, annotation.changeSetLocations());
        }
    }

    private void executeChangesets(TestContext testContext, String[] changeSetLocation) throws SQLException,
            LiquibaseException {
        for (String location : changeSetLocation) {
            DataSource datasource = testContext.getApplicationContext().getBean(DataSource.class);
            DatabaseConnection database = new JdbcConnection(datasource.getConnection());
            Liquibase liquibase = new Liquibase(location, new FileSystemResourceAccessor(), database);
            liquibase.update(null);
        }
    }

}

В журнале ошибок нет, только NullPointerException в моем тесте. Я не вижу, как использование моего TestExecutionListener влияет на автопроводку или инъекцию.


person nansen    schedule 29.03.2013    source источник


Ответы (2)


Я просмотрел журналы spring DEBUG и обнаружил, что, когда я опускаю свой собственный TestExecutionListener spring, он устанавливает DependencyInjectionTestExecutionListener на место. При аннотировании теста с помощью @TestExecutionListeners этот прослушиватель перезаписывается.

Поэтому я просто явно добавил DependencyInjectionTestExecutionListener с моим собственным, и все работает нормально:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "file:src/main/webapp/WEB-INF/applicationContext-test.xml" })
@TestExecutionListeners(listeners = { LiquibaseTestExecutionListener.class,
    DependencyInjectionTestExecutionListener.class })
@LiquibaseChangeSet(changeSetLocations = { "liquibase/v001/createTables.xml" })
public class DeviceDAOTest {
    ...

ОБНОВЛЕНИЕ: поведение задокументировано здесь.

... В качестве альтернативы вы можете полностью отключить внедрение зависимостей, явно настроив свой класс с помощью @TestExecutionListeners и исключив DependencyInjectionTestExecutionListener.class из списка слушателей.

person nansen    schedule 29.03.2013
comment
Это правильно: если вы укажете пользовательский TestExecutionListener через @TestExecutionListeners, вы неявно переопределите все TestExecutionListeners по умолчанию. Конечно, эта функциональность, возможно, не так хорошо документирована. Поэтому не стесняйтесь открывать вопрос JIRA, чтобы запросить улучшение документации. ;) - person Sam Brannen; 30.03.2013
comment
@SamBrannen: На самом деле это задокументировано - возможно, несколько неявно. Смотрите мой обновленный ответ. - person nansen; 30.03.2013
comment
Я хорошо знаю текст, который вы цитируете, так как я написал его. ;) Но... это явно не описывает сценарий, с которым вы столкнулись. Вот почему я предложил вам открыть тикет JIRA, чтобы улучшить документацию. - person Sam Brannen; 26.06.2013
comment
Стоит добавить, что, начиная с Spring 4.1, mergeMode = MergeMode.MERGE_WITH_DEFAULTS можно использовать для включения всех прослушивателей TestExecutionListener по умолчанию. - person Steve Chambers; 18.09.2015

Я бы рекомендовал рассмотреть возможность просто сделать что-то вроде:

@TestExecutionListeners(
        mergeMode =TestExecutionListeners.MergeMode.MERGE_WITH_DEFAULTS,
        listeners = {MySuperfancyListener.class}
)

так что вам не нужно знать, какие слушатели требуются. Я рекомендую этот подход, потому что несколько минут я боролся со SpringBoot, пытаясь заставить его работать правильно, просто используя DependencyInjectionTestExecutionListener.class.

person JeanValjean    schedule 08.01.2017