У меня есть следующий объект JPA
@Entity
class UserEntity {
companion object {
fun fromParameters(uuid: String, email: String, password: String, firstName: String, lastName: String) =
UserEntity().apply {
this.uuid = uuid
this.email = email
this.password = password
this.firstName = firstName
this.lastName = lastName
}
}
@Id
lateinit var uuid: String
@Column(nullable = false, unique = true)
lateinit var email: String
@Column(nullable = false)
lateinit var password: String
@Column(nullable = false)
lateinit var firstName: String
@Column(nullable = false)
lateinit var lastName: String
}
И это мой тест, чтобы проверить ограничение UNIQUE, вставив другого пользователя с тем же адресом электронной почты.
@RunWith(SpringRunner::class)
@DataJpaTest
@AutoConfigureTestEntityManager
class TestUserCrudRepository {
@Autowired
private lateinit var userCrudRepository: UserCrudRepository
private val testUserEntity = UserEntity.fromParameters(
UUID.randomUUID().toString(),
"[email protected]",
"password".toHash(),
"Caetano",
"Veloso"
)
@Test
fun `when email already exists it should throw error`() {
with (userCrudRepository) {
save(testUserEntity)
val newUserEntity = with (testUserEntity) { UserEntity.fromParameters(UUID.randomUUID().toString(), email, password, firstName, lastName) }
shouldThrow<SQLException> { save(newUserEntity) }
}
}
}
Новый объект всегда вставляется с дубликатом электронной почты без каких-либо исключений.
Expected exception java.sql.SQLException but no exception was thrown
Я вижу в журнале, что таблица создана правильно с заданным ограничением.
Hibernate: drop table user_entity if exists
Hibernate: create table user_entity (uuid varchar(255) not null, email varchar(255) not null, first_name varchar(255) not null, last_name varchar(255) not null, password varchar(255) not null, primary key (uuid))
Hibernate: alter table user_entity add constraint UK_4xad1enskw4j1t2866f7sodrx unique (email)
Заранее спасибо!





Это происходит из-за того, что не выдается заявление insert.
Спящий режим не flush сеанс если у него нет веских причин для этого.
@DataJpaTest — это @Transactional. Это означает, что транзакция, в которой выполняется метод @Test, откатывается после возврата метода.UserEntity отображение также побуждает спящий режим задерживать insert (попробуйте использовать @GeneratedValue(strategy = IDENTITY) в свойстве id, чтобы принудительно выдать inserts)Не вдаваясь в подробности, при запуске теста происходит следующее:
begin метод работает@Test - Hibernate понимает, что нет причин обращаться к базе данных и задержкиsave(testUserEntity)insert - то же, что и предыдущийshouldThrow<SQLException> { save(newUserEntity) } метод возвращает@Tests, потому что для этого нет причин.Самый простой способ сделать это — использовать insert:
with (userCrudRepository) {
save(testUserEntity)
val newUserEntity = with (testUserEntity) { UserEntity.fromParameters(UUID.randomUUID().toString(), email, password, firstName, lastName) }
save(newUserEntity)
assertThrows<DataIntegrityViolationException> {
flush()
}
}
Обратите внимание, что в JpaRepository#flush нет метода flush.
Я предполагаю, что вы расширили CrudRepository... Вместо этого вы можете расширить CrudRepository.
См.: В чем разница между интерфейсами CrudRepository и JpaRepository в Spring Data JPA?
Примечание об исключении
Вы ожидаете, что будет выброшено JpaRepository.
Но обратите внимание, что вместо этого будет выбрано SQLException.
См.: Согласованная иерархия исключений
О, я просто пропустил некоторые моменты. @Commit вызовет исключение за пределами из @Test (эту часть ответа я удалил). Вам не нужно использовать @Rollback. Обернуть flush в assertThrows должно быть достаточно. Я смог получить UnexpectedRollbackException, только если @Commit присутствует на @Test. Не могли бы вы рассказать об этом подробнее?
Я рад, что смог вам помочь :)
Отличный ответ, я только начал работать с Spring Boot 3 дня назад :) Аннотирование теста с помощью
@Commitне решает мою проблему, потому что я проверяю исключение до завершения метода тестирования. Я расширилJpaRepository, как вы предлагаете, поймал исключение наflush()и использовал@Rollbackв тесте, чтобы избежать выдачиUnexpectedRollbackException. Возможно, вы захотите обновить свой ответ этим.