如何让DataJpaTest自动刷新保存?

Kor*_*gay 9 spring hibernate spring-data-jpa

我有一个Employee具有以下列的实体:

@Entity
class Employee {
  @Column(name = "first_name", length = 14)
  private String firstName;
Run Code Online (Sandbox Code Playgroud)

我有一个 Spring JPA 存储库:

@Repository
public interface EmployeeRepository extends CrudRepository<Employee, Integer> {
Run Code Online (Sandbox Code Playgroud)

test/resources/application.properties有以下内容,以便使用内存中的 h2 数据库和自动生成的表:

spring.jpa.hibernate.ddl-auto=create
spring.datasource.driver-class-name=org.h2.Driver
spring.datasource.url=jdbc:h2:mem:db;DB_CLOSE_DELAY=-1
spring.datasource.username=sa
spring.datasource.password=sa
Run Code Online (Sandbox Code Playgroud)

我预计这个测试会失败,因为它的firstName长度超过了允许的长度:

@DataJpaTest
public class EmployeeRepositoryTest {

  @Autowired
  private EmployeeRepository employeeRepository;

  @Test
  public void mustNotSaveFirstNameLongerThan14() {
    Employee employee = new Employee();
    employee.setFirstName("koraykoraykoray");  // 15 characters!
    employeeRepository.save(employee);
  }
}
Run Code Online (Sandbox Code Playgroud)

我很惊讶地发现这个测试没有失败,但是以下测试确实失败了:

@DataJpaTest
public class EmployeeRepositoryTest {

  @Autowired
  private EmployeeRepository employeeRepository;

  @Test
  public void testMustNotSaveFirstNameLongerThan14() {
    Employee employee = new Employee();
    employee.setFirstName("koraykoraykoray");  // 15 characters!
    employeeRepository.save(employee);
    employeeRepository.findAll();
  }
}
Run Code Online (Sandbox Code Playgroud)

与堆栈跟踪:

Caused by: org.h2.jdbc.JdbcSQLDataException: Value too long for column "FIRST_NAME VARCHAR(14)": "'koraykoraykoray' (15)"; SQL statement:
Run Code Online (Sandbox Code Playgroud)

唯一的区别是第二个测试有附加employeeRepository.findAll();语句,据我所知,这会强制 Hibernate 刷新。

这对我来说感觉不对,我宁愿希望测试立即失败save

我也可以有

@Autowired
private TestEntityManager testEntityManager;
Run Code Online (Sandbox Code Playgroud)

并打电话

testEntityManager.flush();
Run Code Online (Sandbox Code Playgroud)

但同样,这感觉也不正确。如何在没有任何解决方法或附加语句的情况下使该测试失败?

doc*_*ore 9

在您的情况下,最简单的选项是配置@Transactional注释,强制向数据库发送测试中的所有更改(它只能在特定的测试中使用):

import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import static org.junit.jupiter.api.Assertions.assertThrows;

@Transactional(propagation = Propagation.NOT_SUPPORTED)
@DataJpaTest
public class EmployeeRepositoryTest {

  @Autowired
  private EmployeeRepository employeeRepository;

  @Test
  public void mustNotSaveFirstNameLongerThan14() {
    Employee employee = new Employee();
    employee.setId(1);
    employee.setFirstName("koraykoraykoray");  // 15 characters!
    assertThrows(DataIntegrityViolationException.class, () -> {
        employeeRepository.save(employee);
    });
  }

  @Test
  public void mustSaveFirstNameShorterThan14() {
    Employee employee = new Employee();
    employee.setId(1);
    employee.setFirstName("koraykor");  // 8 characters!
    employeeRepository.save(employee);
  }
}
Run Code Online (Sandbox Code Playgroud)

PD:由于您的存储库定义,我添加了一个简单的Integer属性作为实体的 PK 。Employee

您可以在下图中看到结果:

存储测试


小智 8

感谢医生的回答,我遇到了与OP类似的问题,你的解决方案有所帮助。我决定深入挖掘一下,找出它为什么有效,以防其他人遇到这个问题。

使用@DataJpaTest带注释的测试类,您的类隐式地变为@Transactional默认传播类型Propagation.REQUIRED。这意味着每个测试方法也@Transactional具有相同的默认配置。现在,所有 CRUD 方法也是CrudRepository@Transactional但它与 无关@DataJpaTest- 它们由于实现而具有事务性。哇,这么多交易!

一旦您用 注释整个类(或只是一个测试方法)@Transactional(propagation = Propagation.NOT_SUPPORTED),您的测试方法就不再是@Transactional。但是,测试方法的内部方法(即来自 的 CRUD 操作)CrudRepository仍然是事务性的,这意味着它们将具有自己的事务范围。因此,它们将在执行后立即提交到数据库,因为默认情况下(在使用 HikariCP 连接池的Spring Boot中),自动提交是打开的。自动提交发生在每个 SQL 查询之后。因此测试会如您所期望的那样通过。

我喜欢将事物可视化,所以这是整个过程的可视化: so-63782195

我希望这可以帮到你。图中的 URL: