'How to make DataJpaTest flush save automatically?

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?



Solution 1:[1]

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.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

Solution 2:[2]

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.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 Koray Tugay