Skip to content
thesarfo

Concept

Testing: Repository Layer

Testing Spring Data repositories with @DataJpaTest, the given/when/then (BDD) test structure, and using @BeforeEach to avoid repeating setup.

views 0

Repository layer testing follows the BDD style:

  1. Given — precondition or setup
  2. When — the action or behavior being tested
  3. Then — verify the output
@Test
public 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:

@DataJpaTest
class 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.