How to make DataJpaTest flush save automatically?

Viewed 2378

I have an Employee entity with the following column:

@Entity
class Employee {
  @Column(name = "first_name", length = 14)
  private String firstName;

and I have a Spring JPA Repository for it:

@Repository
public interface EmployeeRepository extends CrudRepository<Employee, Integer> {

In test/resources/application.properties I have the following so that I use an in-memory h2 database with tables auto-generated:

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

I was expecting this test to fail, since the firstName is longer than what is allowed:

@DataJpaTest
public class EmployeeRepositoryTest {

  @Autowired
  private EmployeeRepository employeeRepository;

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

And I was surprised to see this test was not failing, however the following does fail:

@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();
  }
}

with the stacktrace:

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

The only difference is the second test has the additional employeeRepository.findAll(); statement, which forces Hibernate to flush as far as I understand.

This does not feel right to me, I would much rather want the test to fail immediately for save.

I can also have

@Autowired
private TestEntityManager testEntityManager;

and call

testEntityManager.flush();

but again, this does not feel correct either.. How do I make this test fail without any workaround or additional statements?

2 Answers

The easiest option in your case is configure @Transactional annotation, forcing to send database all changes in your tests (it can be used only in specific ones):

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);
  }
}

PD: I have added a simple Integer property as PK of Employee entity due to your repository definition.

You can see the results in the following picture:

Reposity tests

You could use JpaRepository<T,ID> instead of CrudRepository<T,ID>. Something like:

@Repository
public interface EmployeeRepository extends JpaRepository<Employee, Integer>

Then you can use its saveAndFlush() method anywhere you need to send data immediately:

@Test
public void mustNotSaveFirstNameLongerThan14() {
  Employee employee = new Employee();
  employee.setFirstName("koraykoraykoray");  // 15 characters!
  employeeRepository.saveAndFlush(employee);
}

And in code where you would like to have optimization you still can use save() method.

Related