Когда обязательно использовать TestEntityManager в тестировании?

#spring-boot #spring-boot-test

#java #весенняя загрузка #spring-boot-test

Вопрос:

Наличие приложения Spring Boot, работающего с JPA Spring Data и настроенного H2, для примера / академических целей.

Репозиторий определяется как:

 public interface PersonaRepository extends CrudRepository<Persona, String> {

}
 

а затем следующий тест

 @ActiveProfiles(profiles={"h2"})
@DataJpaTest
class PersonaRepositorySliceTests {

    @Autowired
    private PersonaRepository personaRepository;

    @Autowired
    private TestEntityManager testEntityManager;

    @Test
    void countTest() {
        long count = personaRepository.count();
        assertThat(count).isEqualTo(33);

        count = testEntityManager.getEntityManager()
                                 .createQuery("SELECT COUNT(*) FROM Persona p")
                                 .getResultList().size();
        System.out.println("count: "   count);//prints 1
        //assertThat(count).isEqualTo(33);//fails

        count = testEntityManager.getEntityManager()
                                 .createNativeQuery("SELECT COUNT(*) FROM persona")
                                 .getResultList().size();

        System.out.println("count: "   count);//prints 1
        assertThat(count).isEqualTo(33);//fails
    }

}
 

The

 long count = personaRepository.count();
assertThat(count).isEqualTo(33);
 

pass, поэтому @Entity классы были обнаружены, и, конечно schema-h2.sql , data-h2.sql скрипты and были выполнены, как ожидалось.

Теперь путаница в том, почему

  • .createQuery("SELECT COUNT(*) FROM Persona p")
  • .createNativeQuery("SELECT COUNT(*) FROM persona")

Работает, но всегда возвращает 1, почему не 33, как ожидается?

«Кажется», что TestEntityManager каким-то образом не работает с данными, загруженными data-h2.sql файлом.

Вторичный вопрос:

  • Это ожидаемое поведение?

Даже если да, почему возвращается 1, а не 0? или обязательна дополнительная конфигурация?

Я прочитал несколько руководств о @DataJpaTest и TestEntityManager , среди них, основной источник:

Первый — это тест срезов, а второй — альтернатива EntityManager for test. В других руководствах я видел много примеров TestEntityManager работы с persist и последующего извлечения данных (для 1 или более объектов) внутри одного и того же @Test , поэтому вставка выполнялась вручную.

Основной вопрос:

  • Итак, когда необходимо работать с TestEntityManager типом (подходом) по сравнению с пользовательским CrudRepository<T,ID> подходом для целей тестирования?

Ответ №1:

Основная проблема не имеет ничего общего с весенней загрузкой / гибернацией или вашей настройкой тестирования.

Оператор SQL SELECT COUNT(*) FROM Person возвращает одну строку с одним столбцом, который содержит количество подсчитанных строк:

 $ SELECT COUNT(*) FROM Person;

COUNT(*)  
----
4
 

Вот почему вы должны вызывать .getSingleResult() результат для своего собственного или пользовательского запроса:

 assertThat(personRepository.count())
  .isEqualTo(4);

assertThat(testEntityManager.getEntityManager()
                            .createQuery("SELECT COUNT(*) FROM Person p", Long.class)
                            .getSingleResult())
  .isEqualTo(4L);

assertThat(testEntityManager.getEntityManager()
                            .createNativeQuery("SELECT COUNT(*) FROM Person")
                            .getSingleResult())
  .isEqualTo(BigInteger.valueOf(4));
 

Spring Data JPA делает то же самое под капотом для .count() :

 public long count() {
    return (Long)this.em.createQuery(this.getCountQueryString(), Long.class).getSingleResult();
}
 

Вы также можете напрямую вводить EntityManager для своего @DataJpaTest теста и не нуждаться TestEntityManager в этом:

 @DataJpaTest
class ApplicationTests {

  @Autowired
  private TestEntityManager testEntityManager;

  @Autowired
  private EntityManager entityManager;

  @Autowired
  private PersonRepository personRepository;

  @Test
  void contextLoads() {

    assertThat(personRepository.count())
      .isEqualTo(4);

    //With TestEntityManager

    assertThat(testEntityManager.getEntityManager()
                                .createQuery("SELECT COUNT(*) FROM Person p", Long.class)
                                .getSingleResult())
      .isEqualTo(4L);

    assertThat(testEntityManager.getEntityManager()
                                .createNativeQuery("SELECT COUNT(*) FROM Person")
                                .getSingleResult())
      .isEqualTo(BigInteger.valueOf(4));

     //With EntityManager

    assertThat(entityManager.createQuery("SELECT COUNT(*) FROM Person p", Long.class)
                            .getSingleResult())
      .isEqualTo(4L);

    assertThat(entityManager.createNativeQuery("SELECT COUNT(*) FROM Person")
                            .getSingleResult())
      .isEqualTo(BigInteger.valueOf(4));
  }

}
 

TestEntityManager Предоставляет вспомогательные методы, например, для выполнения операции сохранения / очистки / поиска (очистка контекста сохранения в памяти и чтение одного и того же объекта из базы данных) с помощью однострочника. Это TestEntityManager никогда не является обязательным.

Комментарии:

1. Да, вы правы, я так устал, что у меня возникла такая ситуация. Подумайте о том, чтобы немного расширить информацию TestEntityManager о том, когда обязательно или полезно его использовать

2. это TestEntityManager никогда не является обязательным . Это помогает, например, протестировать полный цикл хранения объекта JPA и его извлечения (очистить контекст сохранения в памяти и прочитать тот же объект из базы данных). Более подробную информацию можно найти в Javadoc класса

3. Подумайте о том, чтобы добавить свой комментарий в сообщение. Это дополняет вашу информацию для аудитории.