Spring boot Back-end 개발 시 TDD기반으로 하려고 엄청 노력 중입니다 🥲

기본적으로 바로 비즈니스 개발 진행하기전에 아래와 같은 단계로 진행합니다.

  1. 서비스 메서드 작성

    1. 아래와 같이 설계된 기능 목록을 먼저 나열합니다.
    @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;
        }
    }
    
    
  2. 단위 테스트를 작성합니다.

    @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을 추가하여 모든 테스트 완료 시 롤백을 하게 설정하였습니다.

FlowChart