添加 @Transactional 通过阻止 JPA 获取外键来破坏单元测试

Adding @Transactional is breaking UnitTests by stopping JPA from fetching the foreign keys

我正在尝试提高飞路数据库测试的速度。原来的方法是运行flyway.clear()flyway.migrate()每次测试之间运行。不过现在的问题是我们有一些可能很长的 运行ning flyway 迁移脚本,我们不想在每次测试之间重新 运行 那些。

我一直在尝试使用spring @Transactional 注解在每次测试后回滚数据库,那么我们只需要运行 flyway 一次。

问题是,一旦我围绕测试添加事务,JPA 就会停止获取外键。我假设这是因为嵌套事务没有按照我的预期运行,但我无法弄清楚我配置错误的地方。

我在这里发布了一个示例项目:ExampleSpringJpaHibernatePostgresqltest 有问题(参见 ExampleTest.java)但总而言之:

我有如下带有外键的实体(我删除了不必要的内容以使其更易于阅读):

@Entity
@Table(name = "parent")
public class ParentEntity {
    @Id
    @Column
    private Integer id;

    @Column
    private String name;

    @OneToMany(fetch = FetchType.EAGER)
    @JoinColumn(name = "fk_parent", referencedColumnName = "id")
    private Set<ChildEntity> children;
}
@Entity
@Table(name = "child")
public class ChildEntity {
    @Id
    @Column
    private Integer id;

    @Column
    private String name;

    @Column(name = "fk_parent")
    private Integer fkParent;
}

然后我有一个使用 parentRepository.findAll() 的测试。如果我使用 @Transactional 围绕该测试,则测试失败,因为 parentEntity.getChildren() 为空。尽管没有事务,并且 运行ning flyway clear/migrate 在每次测试之间一切都按预期工作。

    @Test
    @Transactional
    public void testReadWithDependencies() {
        ParentEntity savedParent1 = parentRepo.save(new ParentEntity("parent1"));
        childRepo.save(new ChildEntity("child1", savedParent1.getId()));

        Set<ChildEntity> acutalChildren = parentRepo.findAll().get(0).getChildren();

        // XXX When the tests are run with @Transactional, this assertion fails.
        assertThat(acutalChildren).isNotNull();
    }

parentRepo.findAll 不会从数据库中刷新数据,也不会将子项添加到父项中的子项中。

因此没有子实体分配给父实体。

因此,如果您刷新实体(我注入了 EntityManger),您的测试就会成功:

@Test
@Transactional
public void testReadWithDependencies() {
    assertThat(parentRepo.findAll()).isEmpty();
    assertThat(childRepo.findAll()).isEmpty();

    ParentEntity savedParent1 = parentRepo.save(new ParentEntity("parent1"));
    childRepo.save(new ChildEntity("child1", savedParent1.getId()));

    List<ParentEntity> allParents = parentRepo.findAll();
    assertThat(allParents).hasSize(1);

    // Refresh
    ParentEntity parentEntity = allParents.get(0);
    entityManager.refresh(parentEntity);

    Set<ChildEntity> acutalChildren = parentEntity.getChildren();

    // XXX When the tests are run with @Transactional, this assertion fails.
    assertThat(acutalChildren).isNotNull();

    assertThat(acutalChildren).hasSize(1);
    assertThat(acutalChildren.iterator().next().getName()).isEqualTo("child1");
}