[사용 스택]
- Spring Boot 3.x
- Java 17
- Spring data JPA
- H2
- Junit 5
- mockito
기존에 진행했던 프로젝트를 혼자서 리팩터링 하기로 했습니다.
리팩터링을 진행하면서 기존의 비즈니스 로직을 헤치는 일이 생기지 않도록
테스트 코드를 작성할 필요가 있었습니다.
또한, SpringBootTest를 사용하는 것은 전체를 부팅하면서 시간이 오래 걸린다는 단점이 있어서
단위 테스트에는 적합하지 않다고 생각하고 다른 것들로 대체해서 사용했습니다.
테스트 코드를 작성하는 순서는 다른 클래스들과의 의존성이 가장 낮은 레포지토리부터
서비스, 컨트롤러 순으로 했습니다.
Repository
레포지토리 테스트는 @DataJpaTest 를 사용했습니다.
이를 사용하면 Jpa 테스트에 필요한 빈들만 스캔되고
트랜잭션 어노테이션이 붙고 인메모리 DB로 테스트할 수 있습니다.
레포지토리 단위 테스트의 역할은 테스트할 레포지토리를 호출하여
원하는 결과를 보이는를 확인하는 것입니다.
테스트하기에 앞서 현재 테스트하고자 하는 레포지토리의 엔티티와 연관관계가 있는 엔티티들을
DB에 준비해주는 작업이 필요할 수 있습니다.
이를 테스트 픽스쳐(Test Fixture)라고 합니다.
@BeforeEach 어노테이션을 붙인 setup() 메서드를 통해
각 테스트가 실행되기 전에 필요한 데이터(Fake)를 준비해 줍니다.
(@DataJpaTest에 @Transaction이 포함되므로 데이터베이스를 정리해 주는 것은 필요 없습니다.)
각 테스트는 Given/When/Then 패턴을 사용해서 코드를 구분했습니다.
Given에서는 테스트할 레포지토리의 메서드에 들어갈 매개변수를 준비해 줍니다.
When에서는 해당 메서드를 호출하여 동작시킵니다.
Then에서는 반환된 데이터를 Assertions.assertThat() 메서드를 통해 검증합니다.
저는 Then에서 데이터를 검증하기 위해서
앞서 setup() 메서드를 통해 DB에 준비해 둔 데이터를 필드에 두었습니다.
아래는 제가 위 방법으로 작성한 테스트 코드입니다.
// ....
import static org.assertj.core.api.Assertions.assertThat;
@DataJpaTest
class DeviceRepositoryTest {
// 테스트할 레포지토리
@Autowired
DeviceRepository deviceRepository;
@Autowired
CategoryRepository categoryRepository;
private Device device;
private Category category;
@BeforeEach
void setUp(){
category = Category.createCategory("manufacturer", "deviceType");
categoryRepository.save(category);
device = Device.createDevice("deviceName", 1000, category, LocalDateTime.of(LocalDate.of(2024, Month.JULY, 1), LocalTime.of(0, 0)), "detailId");
deviceRepository.save(device);
}
@Test
public void findSliceBy() throws Exception {
//given
Pageable pageable = createPageable();
//when
Slice<Device> response = deviceRepository.findSliceBy(pageable);
//then
assertThat(response.getContent().get(0).getId()).isEqualTo(device.getId());
}
@Test
public void findSliceByCategory_Id() throws Exception {
//given
Pageable pageable = createPageable();
Long categoryId = category.getId();
//when
Slice<Device> response = deviceRepository.findSliceByCategory_Id(pageable, categoryId);
//then
assertThat(response.getContent().get(0).getId()).isEqualTo(device.getId());
}
@Test
public void searchDeviceByDeviceName() throws Exception {
//given
String keyword = device.getDeviceName();
//when
List<Device> response = deviceRepository.searchDeviceByDeviceName(keyword);
//then
assertThat(response.get(0).getId()).isEqualTo(device.getId());
}
private PageRequest createPageable() {
return PageRequest.of(0, 10);
}
}
내 코드에서 아쉬운 점
setup 메서드에서 준비할 Category와 Device 객체를 직접 만들어주는데,
이 가짜 객체를 만들어주는 것들을 따로 메서드로 만들어 사용했다면
다른 레포지토리 테스트에서도 만들면서 생기는 중복을 해결할 수 있다고 생각합니다.
(PageRequest를 만들어주는 것을 createPageable 메서드로 빼둔 것처럼!!)
결론
레포지토리 테스트의 역할을 생각하며 테스트 코드를 작성해 보았습니다.
다음에는 이 레포지토리를 사용하는 서비스를 테스트한 방법을 소개해보겠습니다!!
'WEB > Spring' 카테고리의 다른 글
단위 테스트 - Controller (feat. 추가적인 단위 테스트의 방향성) (0) | 2024.08.31 |
---|---|
단위 테스트 - Service (feat. 테스트 더블) (0) | 2024.08.28 |
Service의 메서드들이 중복해서 사용하는 로직을 테스트하기 (0) | 2024.08.20 |
RestControllerAdvice로 전역에서 발생된 예외 처리하기. (0) | 2024.05.01 |
Spring Security 프레임워크로 로그인에 성공하면 JWT 발급하기 (0) | 2024.04.17 |