在@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 种可能的解决方案:

  1. 添加 liquibase-core jar 以测试类路径并将其配置为 运行 迁移
  2. 在域实体上声明 @UniqueConstraint 并依赖 Hibernate DDL。