在@DataJpaTest 中测试唯一约束
Testing unique constraint in @DataJpaTest
我编写此测试是为了验证数据库中 Domain.name
的唯一约束。但它不起作用:我希望在 domainRepository.saveAndFlush(domainDuplicate)
操作上抛出异常,但测试成功结束。
@RunWith(SpringRunner::class)
@DataJpaTest
class DomainRepositoryTest {
@Autowired
private lateinit var util: TestEntityManager
@Autowired
private lateinit var domainRepository: DomainRepository
@Test
fun testNonUniqueDomainSave() {
// Arrange
val domain = Domain(name = "name")
util.persist(domain)
util.flush()
util.clear()
val domainDuplicate = domain.copy(id = 0L)
// Act
domainRepository.saveAndFlush(domainDuplicate)
// Exception is expected
}
}
测试日志(缩短):
INFO 13522 --- [ main] o.s.t.c.transaction.TransactionContext : Began transaction (1) for test context [DefaultTestContext@8f8717b testClass = DomainRepositoryTest,...]; transaction manager [org.springframework.orm.jpa.JpaTransactionManager@65f36591]; rollback [true]
Hibernate: insert into domains (name, id) values (?, ?)
Hibernate: insert into domains (name, id) values (?, ?)
Hibernate: insert into domains (name, id) values (?, ?)
INFO 13522 --- [ main] o.s.t.c.transaction.TransactionContext : Rolled back transaction for test: [DefaultTestContext@8f8717b testClass = DomainRepositoryTest, ...], attributes = map[[empty]]]
问:如何解决这个测试?
附加问题:为什么log中有3个insert操作?
数据库:H2
原因是 saveAndFlash()
正在对存在的实体进行更新(是的,方法名称令人困惑..)
如果你想检查你的情况,你需要覆盖 saveAndFlash()
并通过 persist
方法使用 EntityManager
。
这里是 Spring JPA 的覆盖 save()
方法的示例:
@PersistenceContext
private EntityManager em;
@Override
@Transactional
public Domain save(Domain domain) {
if (domain.getId() == null) {
em.persist(domain);
return domain;
} else {
return em.merge(domain);
}
}
测试时数据库初始化有问题:没有唯一约束!我假设 Liquibase 应该 运行 在任何测试之前进行迁移,但事实上,它并没有被配置为这样做。默认情况下,Hibernate DDL 自动更新用于在测试中创建数据库模式。
我能想到 2 种可能的解决方案:
- 添加 liquibase-core jar 以测试类路径并将其配置为 运行 迁移
- 在域实体上声明
@UniqueConstraint
并依赖 Hibernate DDL。
我编写此测试是为了验证数据库中 Domain.name
的唯一约束。但它不起作用:我希望在 domainRepository.saveAndFlush(domainDuplicate)
操作上抛出异常,但测试成功结束。
@RunWith(SpringRunner::class)
@DataJpaTest
class DomainRepositoryTest {
@Autowired
private lateinit var util: TestEntityManager
@Autowired
private lateinit var domainRepository: DomainRepository
@Test
fun testNonUniqueDomainSave() {
// Arrange
val domain = Domain(name = "name")
util.persist(domain)
util.flush()
util.clear()
val domainDuplicate = domain.copy(id = 0L)
// Act
domainRepository.saveAndFlush(domainDuplicate)
// Exception is expected
}
}
测试日志(缩短):
INFO 13522 --- [ main] o.s.t.c.transaction.TransactionContext : Began transaction (1) for test context [DefaultTestContext@8f8717b testClass = DomainRepositoryTest,...]; transaction manager [org.springframework.orm.jpa.JpaTransactionManager@65f36591]; rollback [true]
Hibernate: insert into domains (name, id) values (?, ?)
Hibernate: insert into domains (name, id) values (?, ?)
Hibernate: insert into domains (name, id) values (?, ?)
INFO 13522 --- [ main] o.s.t.c.transaction.TransactionContext : Rolled back transaction for test: [DefaultTestContext@8f8717b testClass = DomainRepositoryTest, ...], attributes = map[[empty]]]
问:如何解决这个测试? 附加问题:为什么log中有3个insert操作?
数据库:H2
原因是 saveAndFlash()
正在对存在的实体进行更新(是的,方法名称令人困惑..)
如果你想检查你的情况,你需要覆盖 saveAndFlash()
并通过 persist
方法使用 EntityManager
。
这里是 Spring JPA 的覆盖 save()
方法的示例:
@PersistenceContext
private EntityManager em;
@Override
@Transactional
public Domain save(Domain domain) {
if (domain.getId() == null) {
em.persist(domain);
return domain;
} else {
return em.merge(domain);
}
}
测试时数据库初始化有问题:没有唯一约束!我假设 Liquibase 应该 运行 在任何测试之前进行迁移,但事实上,它并没有被配置为这样做。默认情况下,Hibernate DDL 自动更新用于在测试中创建数据库模式。
我能想到 2 种可能的解决方案:
- 添加 liquibase-core jar 以测试类路径并将其配置为 运行 迁移
- 在域实体上声明
@UniqueConstraint
并依赖 Hibernate DDL。