포스트

Spring Boot Test - Service 테스트

JUnit 5, Mockito와 함께하는 Service 코드 테스트!

잊어버리지 않기 위해 기록 ✏️


Service 코드가 다음 형태로 구현되어 있을 때,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
@RequiredArgsConstructor  
@Service  
public class TaskService {  
    private final TaskRepository taskRepository;  
    private final TaskConverter taskConverter;  


    public List<TaskResponse> todayPlan(long userId) {  
        var taskEntityList = getTaskEntityListWithThrow(userId);  
          
        return taskEntityList.stream()  
                .map(taskConverter::toResponse)  
                .toList();  
    }  
          
    public List<TaskEntity> getTaskEntityListWithThrow(long userId) {  
        var taskEntityList = taskRepository.findAllByUserIdAndStartedAt_DateAndCompletedAtIsNotNullOrderByStartedAtAsc(  
                userId, LocalDateTime.now()  
        );  
          
        if (taskEntityList.isEmpty()) {  
            throw new ApiException("task is empty");  
        }  
        
        return taskEntityList;  
    }  
}


test 코드 작성

mocking을 통한 service 로직 검증을 목표로 테스트 코드를 작성한다.

로직의 결과를 예상하여 stubbing 후 service 메서드를 실행하여 예상 한 결과로 응답이 떨어지는지 확인하면 된다.!

Mock 관련 어노테이션(@Mock, @InjectMocks 등)을 사용하기 위해 @ExtendWith(MockitoExtension.class)를 추가해주어야 한다.

  • @InjectMocks : 실행 할 클래스(ex. TaskService.java)에 Mock 객체를 주입
  • @Spy : 해당 객체를 Mock 객체로 생성. Stubbing 하지 않은 메서드는 원본 메서드가 실행되도록 해줌
  • @Mock : 해당 객체를 Mock 객체로 생성. Stubbing 하지 않은 메서드는 null이 되어 실행시 null pointer exception 발생
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
@ExtendWith(MockitoExtension.class)  
class TaskServiceTest {  
    
    @InjectMocks TaskService service;  
      
    @Mock TaskRepository repository;  
    @Spy  TaskConverter converter;  
    
    
  
    @DisplayName("task list 조회 - 오늘 날짜 데이터 없음")  
    @Test  
    void todayPlan_is_empty() {  
        // given  
        BDDMockito  
                .given(repository  
                        .findAllByUserIdAndStartedAt_DateAndCompletedAtIsNotNullOrderByStartedAtAsc(  
                                anyLong(), any(LocalDateTime.class)  
                        ))  
                .willReturn(List.of());  
          
          
        // when - then  
        Assertions.assertThrows(  
                ApiException.class,  
                () -> service.todayPlan(1L),  
                "task is empty"  
        );  
          
        BDDMockito  
                .then(repository)  
                .should(Mockito.times(1))  
                .findAllByUserIdAndStartedAt_DateAndCompletedAtIsNotNullOrderByStartedAtAsc(  
                        anyLong(), any(LocalDateTime.class)  
                );  
        BDDMockito  
                .then(service)  
                .should(Mockito.times(1))  
                .getTaskEntityListWithThrow(anyLong());  
        BDDMockito  
                .then(converter)  
                .should(Mockito.times(0))  
                .toResponse(any(TaskEntity.class));  
    }  
  
    @DisplayName("task list 조회 - 오늘 날짜 데이터 있음")  
    @Test  
    void todayPlan_list() {  
        // given  
        long userId = 1L;  
          
        List<TaskEntity> taskEntityList = List.of(  
                getTaskEntity(  
                        1L, userId,  
                        "java - stream",  
                        "stream 실습"  
                ),  
                getTaskEntity(  
                        2L, userId,  
                        "DB - MySQL",  
                        "Docker MySQL 설치, 프로젝트와 연결해보는 실습"  
                )  
        );  
        BDDMockito  
                .given(repository  
                        .findAllByUserIdAndStartedAt_DateAndCompletedAtIsNotNullOrderByStartedAtAsc(  
                                eq(userId), any(LocalDateTime.class)  
                        ))  
                .willReturn(taskEntityList);  
          
        List<TaskResponse> doReturnTaskResponses = taskEntityList.stream()  
                .map(converter::toResponse)  
                .toList();  
          
          
        // when  
        List<TaskResponse> actualTaskResponses = service.todayPlan(userId);  
          
          
        // then  
        BDDMockito  
                .then(repository)  
                .should(Mockito.times(1))  
                .findAllByUserIdAndStartedAt_DateAndCompletedAtIsNotNullOrderByStartedAtAsc(  
                        anyLong(), any(LocalDateTime.class)  
                );  
        BDDMockito  
                .then(converter)  
                .should(Mockito.atLeastOnce())  
                .toResponse(any(TaskEntity.class));  
          
        assertThat(doReturnTaskResponses)  
                .usingRecursiveComparison()  
                .isEqualTo(actualTaskResponses);
    }  
          
      
    private static TaskEntity getTaskEntity(  
            long id, long userId, String title, String description  
    ) {
        return TaskEntity.builder()  
                .id(id)  
                .userId(userId)  
                .title(title)  
                .description(description)  
                .startedAt(LocalDateTime.now())  
                .build();  
    }  
}




참고한 사이트

이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.