#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. Подумайте о том, чтобы добавить свой комментарий в сообщение. Это дополняет вашу информацию для аудитории.