Spring boot Back-end 개발 시 TDD기반으로 하려고 엄청 노력 중입니다 🥲
기본적으로 바로 비즈니스 개발 진행하기전에 아래와 같은 단계로 진행합니다.
서비스 메서드 작성
@Slf4j
@Service
@RequiredArgsConstructor
public class TodosService {
//todo: 11/18일 개발 예정, junit 부터 😎
//todo: createTodo
public TodosEntity createSchedules(TodosDto todosDto) {
return null;
}
//todo: readTodo
public TodosEntity getDetailTodoDetailInfo(Long todosId) {
return null;
}
//todo: readTodo
public List<TodosEntity> getDetailTodosList(TodosDtoSearchDto todosDtoSearchDto) {
return null;
}
//todo: updateTodo
public TodosEntity modifyTodoInfo(Long todosId, TodosDtoSearchDto todosDtoSearchDto) {
return null;
}
//todo: deleteTodo
public String deleteScheduleData(Long todosId){
return null;
}
}
단위 테스트를 작성합니다.
@Slf4j
@Transactional
@SpringBootTest
class TodosServiceTest {
@Autowired
private TodosRepository todosRepository;
private TodosDto todosDto;
@BeforeEach
void beforeEach() {
// given
todosDto = TodosDto.builder()
.task("매일 저녁 toyProject!!")
.dueDate(LocalDate.now().plusDays(100))
.build();
}
@Test
void createSchedules() {
// when
Optional<TodosEntity> optionalTodosEntity = saveTodos();
// then
Assertions.assertAll(
() -> assertTrue(optionalTodosEntity.isPresent()),
() -> assertEquals(todosDto.getTask(), optionalTodosEntity.get().getTask()),
() -> assertEquals(todosDto.getDueDate(), optionalTodosEntity.get().getDueDate())
);
}
@Test
void getDetailTodoDetailInfo() {
// given
Optional<TodosEntity> saveResultEntity = saveTodos();
Long todosId = saveResultEntity.get().getId();
// when
Optional<TodosEntity> searchResultEntity = todosRepository.findById(todosId);
// then
Assertions.assertAll(
() -> assertTrue(searchResultEntity.isPresent()),
() -> assertEquals(searchResultEntity.get().getTask(), saveResultEntity.get().getTask())
);
}
@Test
void getTodosList() {
// given
saveTodos();
TodosDtoSearchDto todosDtoSearchDto =
TodosDtoSearchDto.builder()
.task("매일 저녁 toyProject!!")
.dueDate(LocalDate.now().plusDays(100))
.isCompleted('N')
.build();
// when
List<TodosEntity> todosEntityList = todosRepository.findByDueDate(todosDtoSearchDto.getDueDate());
if (!ObjectUtils.isEmpty(todosDtoSearchDto.getTask())) {
todosEntityList = todosEntityList.stream().filter(item -> item.getTask().contains(todosDtoSearchDto.getTask())).toList();
}
if (!ObjectUtils.isEmpty(todosDtoSearchDto.getIsCompleted())) {
todosEntityList = todosEntityList.stream().filter(item -> item.getIsCompleted() == todosDtoSearchDto.getIsCompleted()).toList();
}
// then
List<TodosEntity> finalTodosEntityList = todosEntityList;
Assertions.assertAll(
() -> Assertions.assertFalse(finalTodosEntityList.isEmpty()),
() -> Assertions.assertTrue(finalTodosEntityList.stream().anyMatch(item -> item.getIsCompleted() == 'N')),
() -> Assertions.assertTrue(finalTodosEntityList.stream().anyMatch(item -> item.getTask().equals(todosDtoSearchDto.getTask())))
);
}
@Test
void modifyTodoInfo() {
// given
Optional<TodosEntity> saveResultEntity = saveTodos();
TodosDto requestTodosDto = TodosDto.builder()
.task("매일 저녁 푸쉬업!!")
.dueDate(LocalDate.now().plusDays(10))
.isCompleted('Y')
.build();
Optional<TodosEntity> targetEntity = todosRepository.findById(saveResultEntity.get().getId());
// when
if (targetEntity.isPresent()) {
if (!ObjectUtils.isEmpty(requestTodosDto.getTask())) {
log.info("task update {}", requestTodosDto.getTask());
targetEntity.get().setTask(requestTodosDto.getTask());
}
if (!ObjectUtils.isEmpty(requestTodosDto.getIsCompleted())) {
log.info("isCompleted update {}", requestTodosDto.getIsCompleted());
targetEntity.get().setIsCompleted(requestTodosDto.getIsCompleted());
}
if (!ObjectUtils.isEmpty(requestTodosDto.getDueDate())) {
log.info("dueDate update {}", requestTodosDto.getDueDate());
targetEntity.get().setDueDate(requestTodosDto.getDueDate());
}
todosRepository.save(targetEntity.get());
} else {
Assertions.fail("데이터가 없습니다!");
}
// then
targetEntity = todosRepository.findById(saveResultEntity.get().getId());
Optional<TodosEntity> finalTargetEntity = targetEntity;
Assertions.assertAll(
() -> Assertions.assertTrue(finalTargetEntity.isPresent()),
() -> Assertions.assertEquals(requestTodosDto.getTask(), finalTargetEntity.get().getTask()),
() -> Assertions.assertEquals(requestTodosDto.getDueDate(), finalTargetEntity.get().getDueDate()),
() -> Assertions.assertEquals(requestTodosDto.getIsCompleted(), finalTargetEntity.get().getIsCompleted())
);
}
@Test
void deleteScheduleData() {
// given
Optional<TodosEntity> saveResultEntity = saveTodos();
Long todosId = saveResultEntity.get().getId();
// when
Optional<TodosEntity> targetEntity = todosRepository.findById(todosId);
if (targetEntity.isPresent()) {
todosRepository.delete(targetEntity.get());
} else {
Assertions.fail("데이터가 없습니다!");
}
//then
targetEntity = todosRepository.findById(todosId);
Assertions.assertTrue(targetEntity.isEmpty());
}
private Optional<TodosEntity> saveTodos() {
return Optional.of(todosRepository.save(
TodosEntity.builder()
.task(todosDto.getTask())
.isCompleted('N')
.dueDate(todosDto.getDueDate())
.userId(1L)
.build()
)
);
}
}
단위 테스트는 항상 실제 데이터에 영향을 주지 않으며 반복이 가능해야 합니다. 📌
단위 테스트 클래스에 @Transactional을 추가하여 모든 테스트 완료 시 롤백을 하게 설정하였습니다.