Как написать тесты junit для JPA, которые можно запускать параллельно? - PullRequest
3 голосов
/ 09 августа 2011

Что мне нужно знать, чтобы написать модульные тесты, которые все обращаются к базе данных (через JPA + Hibernate), чтобы они могли работать параллельно?Мы начали использовать функцию параллельного модульного тестирования mavens, и тесты не выполняются, когда они выполняются в одном потоке.

Я приведу короткий пример того, как мы сейчас пишем наши тесты.Мы устанавливаем EntityManager и другие классы в @Before, начинаем транзакцию и, наконец, вставляем тестовые данные, которые нужны всем нашим тестам в классе (специфичные для теста данные создаются в тесте).@After тест запущен, мы откатываем все обратно, вызывая Transaction.rollback(); и null всех переменных-членов.Мы запускаем все модульные тесты для hsqldb в памяти.

public class TestLogEntryDAO {
    private EntityManager em;
    private LogEntryDAO dao;

    @Before
    public void before() {
        em = Persistence.createEntityManagerFactory("junit")
            .createEntityManager();
        dao = new DAOFactory<LogEntryDAO>(LogEntryDAO.class).newInstance(em,
            Mockito.mock(ILogger.class));

        em.getTransaction().begin();

        RecordDAO recordDAO = new DAOFactory<RecordDAO>(RecordDAO.class)
            .newInstance(em, Mockito.mock(ILogger.class));
        recordDAO.setLogEntryDAO(dao);
        createTestData(); // Is this ok?
    }

    @After
    public void after() {
        em.getTransaction().rollback();
        em = null;
        dao = null;
    }

    @Test
    public void testSomething() {
        // ...
    }
}

Заранее спасибо!

1 Ответ

1 голос
/ 09 августа 2011

Предостережение: я не пытался это сделать. Это одно из моих нереализованных улучшений в моем текущем наборе тестов.


Параллельные тесты с использованием JPA неизбежно завершатся неудачей, если выполняется одно или несколько из следующих условий:

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

Если вам нужно запустить параллельные тесты, вы можете сгруппировать все тесты, которые читают данные, в отдельный набор, но это все равно потребует сериализации тестов, которые изменяют состояние базы данных. Вы можете, если позволяют ваши тесты, использовать отдельные наборы данных для каждого теста, который выполняется параллельно; обратите внимание, что это относится не ко всем приложениям и не ко всем тестам.

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

Кроме того, данные, которые могут быть изменены во время тестов, не должны совместно использоваться тестами. Это легко достижимо, если вы используете разные значения для каждого теста. Вам нужно убедиться, что ваши тесты будут утверждать эти значения или эквивалентное инвариантное свойство, специфичное для теста. Прикрепление данных может быть выполнено с использованием идентификаторов потоков.

Чтобы привести пример вышесказанного, если вы запускаете тесты, которые сохранят сущность A (с атрибутами A1 и A2) в базе данных, тогда тест T1 должен сохранить экземпляр A со значениями A1 = x1 и A2 = y1 и параллельный тест T2, должен сохраняться другой объект A со значениями A1 = x2 и A2 = y2. Это предполагает, что другие тесты, которые выполняются параллельно, изменят значение как A1, так и A2. Если ваши тесты имеют свойство не мутировать А1, то все такие значения А1 могут быть постоянными по всем тестам.

...