본문 바로가기

IT/개발

spring test spy 활용하기

비즈니스로직이 녹아들어간 Service.class에서 간혹
interface를 맴버변수로 DI시키는 경우가 있었습니다.

프로젝트 진행중 일부 소스를 발췌했습니다.
아래 소스중 TalkMessage.class가 interface이고 
TalkMessage를 구현하는 구현체가 2개정도 있었습니다.
그래서 멤버필드를 list로 주입받았습니다.

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
@Service
public class TalkMessageApiClientService {
 
    private final TalkMessageApiClient talkMessageApiClient;
    private final List<TalkMessage> talkMessages; //요놈이 interface
 
    public TalkMessageApiClientService(TalkMessageApiClient talkMessageApiClient,
                                       List<TalkMessage> talkMessages) {
        this.talkMessageApiClient = talkMessageApiClient;
        this.talkMessages = talkMessages;
    }
 
    public List<TalkMessageSenderView> getTalkMessageSendList(TalkMessageRequest req){
        List<FeignTalkMessageResponse> res = getTalkMessageApi(req);
        List<Integer> userIds = res.stream().map(FeignTalkMessageResponse::getReceiverId).distinct().collect(Collectors.toList());
        return getTalkMessageClass(req).sendConvert(userIds, res, req);
    }
 
    public List<TalkMessageReceiveView> getTalkMessageReceiveList(TalkMessageRequest req){
        List<FeignTalkMessageResponse> res = getTalkMessageApi(req);
        List<Integer> userIds = res.stream().map(FeignTalkMessageResponse::getSenderId).distinct().collect(Collectors.toList());
        return getTalkMessageClass(req).receiveConvert(userIds, res, req);
    }
 
    private List<FeignTalkMessageResponse> getTalkMessageApi(TalkMessageRequest req) {
        ApiResponse<List<FeignTalkMessageResponse>> res = Optional.of(talkMessageApiClient.findMessages(
                FeignTalkMessageRequest.builder()
                        .deleteYn(req.getDeleteYn())
                        .isSearching(!req.getKeyword().isEmpty())
                        .partnerIds(req.getKeyword().isEmpty() ? Collections.emptyList() : getTalkMessageClass(req).getPartnerIds(req) )
                        .readYn(req.getConfirmYn())
                        .senderOrReceiver(req.getTabName().toUpperCase())
                        .talkType(req.getTalkType())
                        .teacherId(req.getAdmId())
                        .build(), req.getOffset(), req.getLimit()))
                .filter(data -> !data.isBadRequest())
                .orElseThrow(() -> new ValidationException("API호출 실패했습니다."));
        return res.getData();
    }
 
    private TalkMessage getTalkMessageClass(TalkMessageRequest req){
        return talkMessages.stream()
                .filter(data -> req.getTalkType().equals(data.getTalkType()))
                .findFirst()
                .orElseThrow(() -> new NoDataException("정상적인 호출이 아닙니다."));
    }
}
cs

 

위의 비즈니스 로직에 대해서 Test를 작성하려고 했었습니다.
기존처럼 Mock객체를 주입해서 했었는데
당연하게도 오류가 나면서 테스트가 정상적으로 이뤄지지 않았고

해당interface에 실제 구현되어있는 class를 mock으로 주입해주면서
해당오류는 해결되었으며 테스트도 정상적으로 진행했습니다.

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
@RunWith(MockitoJUnitRunner.class)
public class TalkMessageApiClientServiceTest{
    @Mock
    private TalkMessageApiClient talkMessageApiClient;
    @Spy
    private List<TalkMessage> talkMessages = new ArrayList<>();
    @InjectMocks
    private TalkMessageApiClientService talkMessageApiClientService;
 
    @Mock
    private ParentTalk parentTalk;
    @Mock
    private StudentTalk studentTalk;
 
 
    @Before
    public void setup(){
        given(parentTalk.getTalkType()).willReturn("P");
        given(studentTalk.getTalkType()).willReturn("S");
 
        given(parentTalk.sendConvert(anyList(), anyList(), any())).willReturn(
                Arrays.asList(
                        TalkMessageSenderView.builder().talkType("학부모").talkNo(123L).groupId(234).readYn("N").msgContent("내용1").build(),
                        TalkMessageSenderView.builder().talkType("학부모").talkNo(456L).groupId(567).readYn("N").msgContent("내용2").build()));
 
        given(studentTalk.receiveConvert(anyList(), anyList(), any())).willReturn(
                Arrays.asList(
                        TalkMessageReceiveView.builder().talkType("학생").talkNo(123L).groupId(234).readYn("Y").msgContent("학생받기1").stuId(1111).build(),
                        TalkMessageReceiveView.builder().talkType("학생").talkNo(456L).groupId(567).readYn("Y").msgContent("학생받기2").stuId(2222).build(),
                        TalkMessageReceiveView.builder().talkType("학생").talkNo(789L).groupId(890).readYn("Y").msgContent("학생받기3").stuId(3333).build()
                )
        );
        talkMessages.add(parentTalk);
        talkMessages.add(studentTalk);
    }
 
    @Test
    public void getTalkMessageSendList(){
        given(talkMessageApiClient.findMessages(any(), anyInt(), anyInt())).willReturn(this.getTalkMessage());
 
        List<TalkMessageSenderView> talkMessageSenderViews = talkMessageApiClientService.getTalkMessageSendList(TalkMessageRequest.builder().admId(1).talkType("P").keyword("").searchType("").tabName("SENDER").limit(1000).offset(0).build());
        assertThat(talkMessageSenderViews, is(notNullValue()));
        assertThat(talkMessageSenderViews.size(), is(2));
        assertThat(talkMessageSenderViews.get(0).getTalkNo(), is(123L));
    }
}
 
cs

 

제가 내린 결론?이라고 해야되나...
평소에 @Mock 어노테이션과 @InjectMocks만으로 테스트를 진행했었는데
@Spy 어노테이션은 interface를 주입해주는 
즉 임시객체를 만드는것은 똑같으나 결국은 
다시 주입해줘야 하는 객체를 만들때 사용하면? 되는 어노테이션 같습니다.

감사합니다.

'IT > 개발' 카테고리의 다른 글

spring cloud  (0) 2021.10.12
Kafka  (0) 2021.10.12
JAVA 8 groupingBy, mapping, collectingAndThen  (0) 2021.01.19
spring junit void test  (0) 2020.12.01
spring file delete  (0) 2020.10.29