JUnit, JPA и Spring: как обеспечить, чтобы модульные тесты оставляли базу данных чистой после завершения - PullRequest
2 голосов
/ 07 февраля 2011

Я пытаюсь использовать SpringJunit4ClassRunner для проверки своих классов DAO, не забывая о данных, когда я закончу, используя аннотацию @Transactional. Мой класс DAO содержит (урезанный):

@Repository
public class IdsFunctionJpaController {

  @PersistenceContext
  EntityManager em;

  public void save(IdsFunction function) {
    if (function.getId() == 0) {
      create(function);
    } else {
      update(function);
    }
  }

  @Transactional
  private void create(IdsFunction idsFunction) {
    try {
      em.persist(idsFunction);
    }
    catch (Exception e) {
      System.out.println(e);
    } finally {
      em.close();
    }
  }

  @Transactional
  private void update(IdsFunction function) {
    try {
      em.merge(function);
    } finally {
      em.close();
    }
  } 
}

и мой начальный тестовый блок JUnit

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations={"/applicationContext.xml"} )
public class IdsFunctionJpaControllerTest {

  @Autowired
  IdsFunctionJpaController dao;

  @Test
  @Transactional
  public void addFunction() {
    IdsFunction function = new IdsFunction();
    function.setDescription("Test Function Description");
    dao.save(function);
    assertTrue(function.getId() != 0);
  }
}

То, что я пытаюсь сделать здесь, это просто проверить, что объект был создан, но этот тест не пройден. Если я удаляю аннотацию @Transactional, то тест проходит, но тестовый объект остается в базе данных. Что я делаю не так?

Привет

Ответы [ 2 ]

6 голосов
/ 07 февраля 2011

Механизмы прокси

Вы бьетесь головой о прокси JDK.

Ваш dao.save() метод не транзакционный, и он пытается вызвать транзакционные методы create() и update(). Но транзакционные вещи происходят в JDK-прокси за пределами класса, в то время как метод save уже находится в классе.

См. мой предыдущий ответ для справки.

Решения:

  • сделайте ваш save() метод транзакционным
  • (намного лучше) вообще не делают ваши DAO транзакционными. Транзакции принадлежат уровню обслуживания, а не уровню DAO.

Справка:


Обновление: Я был сбит с толку присутствием запутанных аннотаций @Transactional в методах Дао. Вы должны удалить их, они ничего не делают и сбивают с толку людей.

Как вы можете прочитать по ссылкам, которые я разместил выше, аннотации @Transactional действуют только тогда, когда они присутствуют в общедоступных методах, которые будут вызываться извне bean-компонента Spring (поэтому вы не можете иметь один метод в классе делегирует один или несколько прокси-методов одного и того же класса).


Транзакционные тесты

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

5 голосов
/ 07 февраля 2011

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

Введите EntityManager в ваш тестовый класс и после сохранения сохраните em.flush().

Кроме того, ваш уровень DAO не должен быть транзакционным.Транзакции, как правило, имеют смысл только на уровне сервиса.

Редактировать:

На самом деле, ваш DAO также полностью ошибочен, что этот тест не сможет показать.Эти транзакционные аннотации не будут иметь никакого эффекта, поскольку они являются внутренними вызовами методов.Вы также никогда не должны закрывать EntityManager самостоятельно - контейнер (Spring) сделает это за вас.Кроме того, не перехватывайте общие Exception s, а когда вы не регистрируетесь и игнорируете их.Исключения должны распространяться на уровень обслуживания, где они должны обрабатываться должным образом.Кроме того, не печатайте на стандартный вывод, используйте правильную структуру ведения журналов.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...