TDD с HSQLDB - удаление внешних ключей - PullRequest
9 голосов
/ 21 апреля 2010

Я использую HSQLDB для тестирования интеграции уровня данных, и это здорово. Однако я обнаружил, что мои ограничения по внешнему ключу мешают моим тестам. Например, чтобы протестировать простой выбор в одной таблице, мне нужно вставить фиктивные данные в пять дополнительных таблиц. Это заставляет меня хотеть бросать вещи.

У меня есть аннотации JPA по всему коду модели, и я настроил Hibernate для воссоздания схемы (hbm2ddl.create-drop) в конфигурации. Соединения правильно интерпретируются как ограничения внешнего ключа при создании таблиц.

То, что я хотел бы, это либо:

  1. Первоначально не создавать внешние ключи (идеальные, самые чистые) или
  2. Найдите способ программно отбросить все внешние ключи в базе данных (немного странно, но выполнит свою работу)

Если это полезно, я использую Spring для автоматического подключения этих тестов. Эти тесты наследуются от AbstractTransactionalJUnit4SpringContextTests .

Что вы думаете? Можно ли это сделать?

Ответы [ 5 ]

9 голосов
/ 22 апреля 2010

Вы можете деактивировать ограничения FK с помощью следующей инструкции:

SET REFERENTIAL_INTEGRITY FALSE;

Вы можете выполнить его с помощью JDBC Statement до ваших методов тестирования (и установить обратно на TRUE после).

8 голосов
/ 25 апреля 2012

Я столкнулся с точно такой же проблемой, пытаясь проверить мой DAO с плоским набором данных xml.Конфигурация DBunit + HSQLDB 2.2.8 + JUnit4 + Spring + JPA-> все вместе приводит к

java.sql.SQLIntegrityConstraintViolationException: integrity constraint violation: foreign key no parent; FK13EE6CE6F09A6AAC table: ****

Я нашел хороший обходной путь, реализовав слушателя, расширяющего AbstractTestExecutionListener.Вы должны указать тип действия, которое будет выполняться перед каждым тестом, в нашем случае отключение ограничений внешнего ключа. ПРИМЕЧАНИЕ: синтаксис может отличаться в зависимости от используемой версии HSQLDB.

public class ForeignKeyDisabling extends AbstractTestExecutionListener {    
    @Override
    public void beforeTestClass(TestContext testContext) throws Exception {
        IDatabaseConnection dbConn = new DatabaseDataSourceConnection(
                testContext.getApplicationContext().getBean(DataSource.class)
                );
        dbConn.getConnection().prepareStatement("SET DATABASE REFERENTIAL INTEGRITY FALSE").execute();

    }
}

Затем вам нужно только добавить этот прослушиватель в коллекцию, уже имеющуюся в ваших тестах:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration({"applicationContext-test.xml"})
@TestExecutionListeners({DependencyInjectionTestExecutionListener.class, DataSetTestExecutionListener.class, ForeignKeyDisabling.class})
2 голосов
/ 17 марта 2015

Основываясь на вдохновении в этой теме, я создал несколько более надежное решение для этой проблемы. Дело в том, что мне действительно нравятся ограничения при запуске теста, а все остальные решения просто отключают его. Этот код отключит их только на время импорта набора данных, а затем снова включит их. И может быть легко расширен для поддержки другого механизма БД:

import com.github.springtestdbunit.DbUnitTestExecutionListener;
import org.apache.log4j.Logger;
import org.dbunit.database.DatabaseDataSourceConnection;
import org.dbunit.database.IDatabaseConnection;
import org.springframework.test.context.TestContext;

import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;

/**
 * Class DisableForeignKeysDbUnitTestExecutionListener
 * Simple wrapper class around DbUnitTestExecutionListener, which - for the time of importing the database -
 * disables Foreign Key Constraints checks.
 * This class can be extended by simply overriding toggleForeignKeysConstraintsForDbEngine(Connection, String, boolean);
 * subclasses should always call super-implementation for default case.
 */
public class DisableForeignKeysDbUnitTestExecutionListener
    extends DbUnitTestExecutionListener
{
    private static final Logger logger = Logger.getLogger(DisableForeignKeysDbUnitTestExecutionListener.class);
    private Connection cachedDbConnection;

    @Override
    public void beforeTestMethod(TestContext testContext)
        throws Exception
    {
        this.toggleForeignKeysConstraints(testContext, false);
        super.beforeTestMethod(testContext);
        this.toggleForeignKeysConstraints(testContext, true);
    }

    /**
     * Method should perform query to disable foreign keys constraints or return false,
     * if it is not able to perform such query (e.g. unknown database engine)
     *
     * @param connection    Database connection
     * @param dbProductName Name of the database product (as reported by connection metadata)
     * @param enabled       Expected state of foreign keys after the call
     *
     * @return True, if there was suitable statement for specified engine, otherwise false
     *
     * @throws SQLException
     */
    protected boolean toggleForeignKeysConstraintsForDbEngine(Connection connection, String dbProductName, boolean enabled)
        throws SQLException
    {
        switch (dbProductName)
        {
            case "HSQL Database Engine":
                connection.prepareStatement("SET DATABASE REFERENTIAL INTEGRITY " + (enabled ? "TRUE" : "FALSE"))
                          .execute();
                return (true);
        }
        return (false);
    }

    private void toggleForeignKeysConstraints(TestContext testContext, boolean enabled)
    {
        try
        {
            Connection connection = this.getDatabaseConnection(testContext);
            String databaseProductName = connection.getMetaData().getDatabaseProductName();
            if (!this.toggleForeignKeysConstraintsForDbEngine(connection, databaseProductName, enabled))
            {
                throw new IllegalStateException("Unknown database engine '" + databaseProductName +
                                                    "'. Unable to toggle foreign keys constraints.");
            }
        }
        catch (Throwable throwable)
        {
            logger.error("Unable to toggle Foreign keys constraints: " + throwable.getLocalizedMessage());
        }
    }

    synchronized private Connection getDatabaseConnection(TestContext testContext)
        throws SQLException
    {
        if (this.cachedDbConnection == null)
        {
            DataSource dataSource = testContext.getApplicationContext().getBean(DataSource.class);
            if (dataSource == null)
            {
                throw new IllegalStateException("Unable to obtain DataSource from ApplicationContext. " +
                                                    "Foreign constraints will not be disabled.");
            }

            IDatabaseConnection dsConnection = new DatabaseDataSourceConnection(dataSource);
            this.cachedDbConnection = dsConnection.getConnection();
        }

        return (this.cachedDbConnection);
    }
}
0 голосов
/ 11 ноября 2013

Сделайте это быстро:

SET REFERENTIAL_INTEGRITY FALSE; в файл import.sql в каталоге тестовых ресурсов.

Проблема решена быстро и красиво :)

Вот некоторая информация о import.sql http://christopherlakey.com/articles/import-sql.html

0 голосов
/ 22 апреля 2010

Я бы подумал потратить некоторое время на создание пары приборов, возможно, с DBUnit, который вы вставляете @ Before.

Кстати, AbstractTransactionalJUnit4Test устарел в Spring 3.0

...