首页 > 解决方案 > 如何防止测试中违反参照完整性约束?

问题描述

我使用 JUnit 5 为我的 Spring Boot 应用程序创建单元测试。

对于每个测试类,我使用 @Sql 加载上下文:

HierarchyEntityServiceTest.java

@SpringBootTest
@ActiveProfiles("test")
@Sql({
  "/sql/import_hierarchy_entity_type.sql",
  "/sql/import_hierarchy_entity.sql",
  "/sql/import_site_department.sql"
})
@Transactional
public class HierarchyEntityServiceTest {

  @Autowired
  private HierarchyEntityService hierarchyEntityService;

  @Test
  void findAllSkillCenters() {
    assertThat(hierarchyEntityService.findAllSkillCenters())
      .hasSize(5);
  }

  @Test
  void findDepartmentsBySiteAndSkillCenter() {
    assertThat(hierarchyEntityService.findDepartmentsBySitesAndSkillCenter(
      List.of(11L),
      "DPT_SWFC1"
    )).hasSize(1);
  }
}

SourceDetailsRepositoryTest.java

@SpringBootTest
@ActiveProfiles("test")
@Sql({
  "/sql/import_hierarchy_entity_type.sql",
  "/sql/import_hierarchy_entity.sql",
  "/sql/import_source_details.sql",
  "/sql/import_candidate.sql"
})
@Transactional
class SourceDetailsRepositoryTest {

  @Autowired
  private CandidateRepository candidateRepository;
  @Autowired
  private SourceDetailsRepository sourceDetailsRepository;

  @Test
  void findAllSourceDetailsTest() {
    List<SourceDetails> sourceDetails = sourceDetailsRepository.findAll();
    assertEquals(10, sourceDetails.size());
    assertEquals("SOURCING", sourceDetails.get(0).getType());
  }

  @Test
  void findSourceDetailsByTypeTest() {
    List<SourceDetails> sourceDetails = sourceDetailsRepository.findByType("SOURCING");
    assertEquals(7, sourceDetails.size());
  }

  @Test
  void findSourceDetailByCandidateIdTest() {
    Optional<Candidate> candidate = candidateRepository.findById(1000L);
    Optional<SourceDetails> sourceDetails = sourceDetailsRepository.findById(candidate.get().getSourceDetailsId());
    assertEquals(6L, sourceDetails.get().getId());
  }

}

如您所见,一些 SQL 脚本在多个类中被调用。

这些脚本都包含数据库中的数据:

import_hierarchy_entity.sql

INSERT INTO public.hierarchy_entity(functional_code, label, short_label, type_functional_code,
                                    parent_entity_functional_code)
VALUES ('CC_EDGE_EMB', 'CC Edge & Embedded', 'EE', 'CC', 'DIL'),
       ('DPT_AD1', 'AD1', 'AD1', 'DEPT', 'CC_AUG_DATA'),
       ('DPT_UE2', 'UE2', 'UE2', 'DEPT', 'CC_USER_EFF'),
       ('DPT_UE3', 'UE3', 'UE3', 'DEPT', 'CC_USER_EFF'),
       ('DPT_UE4', 'UE4', 'UE4', 'DEPT', 'CC_USER_EFF'),
       ('DPT_UE5', 'UE5', 'UE5', 'DEPT', 'CC_USER_EFF');

import_candidate.sql

INSERT INTO public.candidate(id, first_name, last_name, sex, status, author_id, manager_id, profile_id, site_id,
                             authored_at, edited_at, is_deleted, arrival_on, years_of_experience, source_id,
                             source_details, source_details_id, dept_code, entity_code)
VALUES (1000, 'JEAN', 'Louis', 'MALE', 'PREQUALIFICATION', 'admin', 'operationnel', 1, 2, CURRENT_TIMESTAMP(),
        CURRENT_TIMESTAMP(), false, CURRENT_TIMESTAMP(), 1, 1, 'Viadeo', 6, 'DPT_EE1', 'CC_EDGE_EMB');

要记住的一件事是,有些表有一个功能代码,它是一个字符串,作为主键。

这是用于我的测试的 Spring Boot 配置文件的配置:

应用程序.yml

spring:
  profiles: test
  datasource:
    driver-class-name: org.h2.Driver
    url: jdbc:h2:mem:db;DB_CLOSE_DELAY=-1
    username: sa
    password:
  data:
    classpath: import.sql

  jpa:
    database-platform: org.hibernate.dialect.H2Dialect
    generate-ddl: true
    hibernate:
      default_schema: public
      ddl-auto: create
      show-sql: true
  flyway:
    enabled: false

当我单独运行测试时,它工作正常。但是,当我运行命令时mvn test,我会遇到相同类型的失败:

Caused by: org.h2.jdbc.JdbcSQLIntegrityConstraintViolationException: 
Intégrité référentielle violation de contrainte: "FK87J5L5FKHUUWS58S4QUJU65O6: PUBLIC.CANDIDATE FOREIGN KEY(AUTHOR_ID) REFERENCES PUBLIC.MYREC_USER(ID) ('admin')"
Referential integrity constraint violation: "FK87J5L5FKHUUWS58S4QUJU65O6: PUBLIC.CANDIDATE FOREIGN KEY(AUTHOR_ID) REFERENCES PUBLIC.MYREC_USER(ID) ('admin')"; SQL statement:
INSERT INTO public.candidate(id, first_name, last_name, sex, status, author_id, manager_id, profile_id, site_id, authored_at, edited_at, is_deleted, arrival_on, years_of_experience, source_id, source_details, source_details_id, dept_code, entity_code) VALUES (1000, 'JEAN', 'Louis', 'MALE', 'PREQUALIFICATION', 'admin', 'operationnel', 1, 2, CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP(), false, CURRENT_TIMESTAMP(), 1, 1, 'Viadeo', 6, 'DPT_EE1', 'CC_EDGE_EMB') [23506-200]
[ERROR] findAllSourceDetailsTest  Time elapsed: 0.09 s  <<< ERROR!

当一个类尝试加载 SQL 脚本时,数据库似乎不干净。此外,文件import.sql似乎与此无关,因为它不加载相同的数据,并且当我删除它时问题仍然存在。

在每个测试类调用之前不应该清理上下文吗?

是否可以对多个测试类继续使用相同的脚本并避免这种情况?

标签: javaspring-bootjunit5referential-integrity

解决方案


您可以使用以下内容来注释您的测试类,而不是 @Sql 注释。您可以为各自的测试类提供适当的 sql 脚本列表。

@SqlGroup({@Sql(executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD, scripts = {"classpath:schema.sql", "import_hierarchy_entity_type.sql", "import_hierarchy_entity.sql", "import_source_details.sql", "import_candidate.sql"})})

我添加了 schema.sql 作为示例,以防您想在每次测试后删除并重新创建架构。代码要做的是在每个测试方法之前执行脚本。

注意:您还可以将 @Transactional 替换为 @DataJpaTest 以获得更多 JPA 支持。


推荐阅读