Несколько встроенных баз данных HSQLDB в ошибках jUnit во время сборки

Я работаю над новым приложением Spring Batch (3.0.3.RELEASE), в котором во время заданий будет осуществляться доступ к нескольким базам данных. Для тестирования мы используем HSQLDB (2.3.2) в качестве встроенной базы данных.

В моем контексте приложения у меня есть следующее.

<jdbc:embedded-database id="dataSource">
</jdbc:embedded-database>

<jdbc:embedded-database id="proDataSource">
    <jdbc:script location="classpath:script-tables.sql" />
    <jdbc:script location="classpath:script-constraints.sql" />
</jdbc:embedded-database>

<jdbc:embedded-database id="altDataSource">
    <jdbc:script location="classpath:script-alt-tables.sql" />
</jdbc:embedded-database>

Когда я запускаю один тест в Eclipse, все в порядке. Когда я собираю из командной строки, после первого теста я получаю ошибки

Failed to execute SQL script statement at line 3 of resource class path resource [script-promrkt-promo.sql]
object name already exists: PROMRKT

Мне кажется, что процесс заполнения в EmbeddedDatabaseFactory получает уже заполненную базу данных. Из того, что я могу сказать, так это то, что после каждого теста не выполняется SHUTDOWN, и HSQLDB оставляет уже заполненную базу данных в памяти.

Я повторно просмотрел документацию и в Spring Doc показывает явную команду выключения. Но если Spring запускает встроенную базу данных при запуске моего теста, почему он не закрывает ее после завершения теста?

  1. Ожидается ли, что встроенные базы данных останутся после каждого модульного теста для одного и того же контекста приложения?
  2. В каком порядке Spring запускает встроенную базу данных и когда инициализируется транзакционный контекст?
  3. Мне нужно использовать очиститель базы данных?
  4. Можно ли обновить заполнение так, чтобы оно заполнялось только при первом запуске базы данных, и откат к исходной конфигурации скрипта, когда мой тест будет завершен (вроде того, как работали AbstractTransactionalSpringContextTests)
  5. Нужны ли мне маркеры транзакций? JobRepo Spring Batch правильно заполняется и уничтожается между каждым тестом. Почему нет моих пользовательских источников данных?

person grbonk    schedule 26.01.2015    source источник
comment
Просто чтобы подтвердить, вы не используете какой-либо пул соединений во время теста? Я спрашиваю из-за этой проблемы: jira.spring.io/browse/SPR-11372   -  person Michael Minella    schedule 26.01.2015
comment
Нет, пулы подключений. По крайней мере, я не сказал прямо о пуле. Я прочитал вопрос, и это может быть связано. Не сулит ничего хорошего, если один из комментариев рекомендует не более одной встроенной БД за раз. Любые идеи о том, где я могу реализовать интерфейс Spring, чтобы исправить / улучшить это поведение?   -  person grbonk    schedule 27.01.2015


Ответы (3)


Сценарий, на который жалуется сообщение журнала, отсутствует в вашей конфигурации. Я полагаю, это выполняется где-то еще? Если это так, вам, вероятно, потребуется добавить @DirtiesContext в свои тесты, чтобы Spring не кэшировал контекст (я предполагаю, что вы используете SpringJunit4Runner с @ContextConfiguration, но не можете быть уверены, поскольку ваш фактический тест не т в вопросе).

Если мое предположение верно, Spring кэширует контекст, чтобы повысить производительность при запуске набора модульных тестов. Если ваш тест изменяет контекст таким образом, что это может повлиять на другие тесты (например, запуск скриптов в одном тесте, который нужно запускать снова в других), вы помечаете тесты с помощью @DirtiesContext, и Spring не будет кэшировать контекст. Вы можете использовать аннотацию на уровне метода или класса. Подробнее об аннотации можно прочитать здесь: http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/test/annotation/DirtiesContext.html

person Michael Minella    schedule 27.01.2015

Я потратил много времени, изучая это, читая документацию Spring Framework (ах!) И просматривая код. В Spring Core 4.1 есть несколько интересных изменений, особенно в тестировании.

Я обнаружил, что ApplicationContext (s) теперь кэшируется на уровне JVM. Если второй тест запрашивает контекст, TestContext сначала просматривает свой кеш, чтобы увидеть, не запрашивал ли уже какой-либо другой тест идентичную конфигурацию.

У меня есть профили для некоторых моих тестов. Тест с другим профилем, но с тем же @ContextConfiguration вызывает повторную загрузку этого контекста с примененным профилем. Когда «Bean Loader» прибывает для создания встроенных баз данных, EmbeddedDatabaseFactory не принимает во внимание, что встроенная база данных (в памяти HSQLDB), возможно, уже была создана или кэширована из предыдущих тестов и не требует повторной инициализации.

Поэтому я добавил некоторую логику в EmbeddedDatabaseFactory.initDatabase (), проверяющую, существует ли уже база данных перед повторной инициализацией и запуском DatabasePopulator.

    List existingDataBases = org.hsqldb.DatabaseManager.getDatabaseURIs();

    boolean isExisting = false;
    String localDBName = StringUtils.lowerCase(this.databaseName);

    for (Object object : existingDataBases) {
        if (object.toString().contains(localDBName)) {
            isExisting = true;
            break;
        }
    }

    // Now populate the database
    if (!isExisting && this.databasePopulator != null) {

(конечно, это не совсем кошерно для того, что понадобится пружине, но это дает понять)

На мой взгляд, это похоже на частичную проблему с механизмом кэширования EmbeddedDatabaseFactory и TestContext. Мои определения "jdbc: embedded-database" не имеют связанных с ними профилей. Почему в кеше необходимо воссоздавать их, а не загружать из существующих кэшированных bean-компонентов?

person grbonk    schedule 29.01.2015

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

Вот пример:

embeddedDatabase = new EmbeddedDatabaseBuilder()
            .setType(EmbeddedDatabaseType.H2)
            .generateUniqueName(true)
            .addScripts("db/sql/create-db.sql", "db/sql/insert-data.sql")
            .build();
person Marek J    schedule 24.02.2016