Repository layer testing follows the BDD style:
- Given — precondition or setup
- When — the action or behavior being tested
- Then — verify the output
@Testpublic void givenEmployeeObject_whenSave_thenReturnSavedEmployee() { // given - precondition or setup Employee employee = Employee.builder() .firstName("Ernest") .lastName("Sarfo") .email("ernestsarfo@gmail.com") .build();
// when - action or the behaviour that we are going to test Employee savedEmployee = employeeRepository.save(employee);
// then - verify the output assertThat(savedEmployee).isNotNull(); assertThat(savedEmployee.getId()).isGreaterThan(0);}Spring Boot’s @DataJpaTest annotation autoconfigures an in-memory database for testing the
persistence layer — instead of connecting to a real database, it sets one up for you, so you
don’t need to mock dependencies. It goes on the class testing the repository.
Note: @DataJpaTest doesn’t load other Spring beans (@Component, @Controller, @Service)
into the application context. By default it scans for @Entity classes and configures
@Repository-annotated Spring Data repositories. Tests are transactional by default and roll
back after each test — though it can test anything persistence-related, like DataSource,
JdbcTemplate, and EntityManager.
Custom query methods (e.g. findByEmail()), JPQL queries with index or named parameters, and
native SQL queries can all be tested the same way. A full test class covering save, find-all,
find-by-id, find-by-email, update, delete, and custom JPQL/native-SQL queries:
@DataJpaTestclass EmployeeRepositoryTests {
@Autowired private EmployeeRepository employeeRepository;
private Employee employee;
@BeforeEach public void setUp() { employee = Employee.builder() .firstName("sarfo") .lastName("kofi") .email("native@gmail.com") .build(); }
@DisplayName("Unit test for save employee operation") @Test void givenEmployeeObject_whenSave_thenReturnSavedEmployee() { Employee savedEmployee = employeeRepository.save(employee);
assertThat(savedEmployee).isNotNull(); assertThat(savedEmployee.getId()).isPositive(); }
@DisplayName("Unit test for get employee operation") @Test void givenEmployeesList_whenFindAll_thenReturnEmployeesList() { Employee employee1 = Employee.builder() .firstName("hoha") .lastName("aja") .email("hohaaja@gmail.com") .build();
employeeRepository.save(employee); employeeRepository.save(employee1);
List<Employee> gotEmployees = employeeRepository.findAll();
assertThat(gotEmployees).isNotNull(); assertThat(gotEmployees.size()).isEqualTo(2); }
@DisplayName("Unit test for get employee by id") @Test void givenSavedEmployee_whenFindById_thenReturnEmployee() { employeeRepository.save(employee);
Employee gottenEmployee = employeeRepository.findById(employee.getId()).get();
assertThat(gottenEmployee).isNotNull(); }
@DisplayName("Unit test for get employee by email operation") @Test void givenEmployeeEmail_whenFindByEmail_thenReturnEmployeeObject() { employeeRepository.save(employee);
Employee gottenEmployee = employeeRepository.findByEmail(employee.getEmail()).get();
assertThat(employee.getEmail()).isEqualTo(gottenEmployee.getEmail()); }
@DisplayName("Unit test for update employee operation") @Test void givenEmployeeObject_whenUpdateEmployee_thenReturnUpdatedEmployee() { employeeRepository.save(employee);
Employee gottenEmployee = employeeRepository.findById(employee.getId()).get(); gottenEmployee.setFirstName("konadu"); gottenEmployee.setEmail("updatedemail@gmail.com"); Employee employeeUpdated = employeeRepository.save(gottenEmployee);
assertThat(employeeUpdated).isNotNull(); assertThat(employeeUpdated.getEmail()).isEqualTo("updatedemail@gmail.com"); assertThat(employeeUpdated.getFirstName()).isEqualTo("konadu"); }
@DisplayName("Unit test for delete employee operation") @Test void givenEmployeeObject_whenDeleteEmployee_thenReturnNothing() { employeeRepository.save(employee);
employeeRepository.delete(employee); Optional<Employee> gottenEmployee = employeeRepository.findById(employee.getId());
assertThat(gottenEmployee).isEmpty(); }
@DisplayName("Unit test for custom query using jpql with index") @Test void givenFirstNameAndLastName_whenFindByJPQL_thenReturnEmployeeObject() { employeeRepository.save(employee); String firstName = employee.getFirstName(); String lastName = employee.getLastName();
Employee gottenEmployee = employeeRepository.findByJPQL(firstName, lastName);
assertThat(gottenEmployee).isNotNull(); }
@DisplayName("Unit test for custom query using jpql with named params") @Test void givenFirstNameAndLastName_whenFindByJPQLNamedParams_thenReturnEmployeeObject() { employeeRepository.save(employee); String firstName = employee.getFirstName(); String lastName = employee.getLastName();
Employee gottenEmployee = employeeRepository.findByJPQLNamedParams(firstName, lastName);
assertThat(gottenEmployee).isNotNull(); }
@DisplayName("Unit test for custom query using native SQL with index parameters") @Test void givenFirstNameAndLastName_whenFindByNativeSQL_thenReturnEmployeeObject() { employeeRepository.save(employee);
Employee gottenEmployee = employeeRepository.findByNativeSQL(employee.getFirstName(), employee.getLastName());
assertThat(gottenEmployee).isNotNull(); assertThat(gottenEmployee.getFirstName()).isEqualTo(employee.getFirstName()); }
@DisplayName("Unit test for custom query using native SQL with named parameters") @Test void givenFirstNameAndLastName_whenFindBySQLNamed_thenReturnEmployeeObject() { employeeRepository.save(employee);
Employee gottenEmployee = employeeRepository.findByNativeSQL(employee.getFirstName(), employee.getLastName());
assertThat(gottenEmployee).isNotNull(); assertThat(gottenEmployee.getFirstName()).isEqualTo(employee.getFirstName()); }}Creating the employee fixture inside every test method is repetitive. JUnit’s @BeforeEach
signals that a method should run before every @Test in the class — that’s what setUp() does
above, so each test just reaches for employee directly instead of rebuilding it.