현재 상황
현재 "반려 하루" 서비스를 개발하면서 Controller, Service, Entity 단위 테스트를 작성하면서 진행하고 있습니다.
이전 서비스를 개발하면서 테스트 코드를 작성하지 않았는데, 테스트를 하기 위해 매번 서버를 띄워서 postman으로 요청을 생성하는 건 너무나 비효율적이었습니다.
또한, 서비스 요구사항의 변경에 대처하기 위함이었습니다.
한번, 두번일 경우 기억을 해서 로직을 수정할 수 있지만 계속해서 요구사항이 변경되었을 때 코드를 수정하면서도 잘 동작하는지 믿을 수 없는 상황을 마주쳤습니다.
따라서, 테스트 코드를 작성하는 연습을 하고 있습니다.
1. 기능을 개발해도, 요구사항이 지속적으로 바뀌기 때문에 코드 변경으로 인한 발생 가능한 결함을 사전에 예방할 수 있고
2. 테스트 코드 자체로 기능을 설명하는 문서가 될 수도 있고
3. 제가 가정한 상황에서 실패하는 테스트가 정상적으로 동작하는지 확인할 수 있어서 코드에 대한 신뢰성을 얻을 수도 있다고 생각합니다.
4. 그리고 테스트가 통과하면 기분이 좋습니다 ㅎㅎ
Mockito를 이용한 단위 테스트
저는 작성한 로직에 대해 검증을 할 수 있도록 Mock 객체를 이용한 단위 테스트를 작성했습니다.
각 객체 간 의존성을 Mock 객체를 이용해서 해결하고 제가 작성한 로직의 상황에 대해서만 정상적으로 동작하는지 확인하기 용이했습니다.
이번 포스팅에서 공유할 내용은 @Mock 객체의 void method를 mocking 하는 도중 마주친 문제점에 대해 작성하려 합니다.
Method Mocking
크게 4가지로 나눌 수 있을 것 같습니다.
1. Return Non void Type
이 경우는 BDDMockito의 given - willReturn을 이용해서 해당 메서드가 동작하면 해당 객체를 return 할 것이라 mocking 해주면 됩니다.
given(Mocking하는 클래스.메서드(any())).willReturn(반환하는 객체);
2. Non void Type and Throw
given(Mocking하는 클래스.메서드(any())).willThrow / thenThrow (반환하는 객체);
3. Return void Type
4. void Type and Throw
저는 3, 4번의 케이스였고, 해당 케이스의 테스트를 작성한 과정을 적어보려 합니다.
마주친 상황
Controller 테스트에서 중복 닉네임을 검증하는 API 테스트를 작성하고 있었습니다. 중복되는 닉네임일 경우 Exception을 발생시키는데, 저는 해당 메서드에 대해 테스트 코드를 작성하고 싶었습니다.
// MemberService.java
public void validateMemberNickName(ValidateMemberNicknameRequest dto) {
if (memberRepository.findByNickName(dto.getNickName()).isPresent()) {
throw new DuplicateMemberNicknameException();
}
}
해당 메서드는 예외를 터트리지 않는 경우 리턴 값이 없습니다.
Controller 테스트에서 Service의 해당 메서드를 Mocking 한다면?
// MemberControllerTest.java
given(memberService.validateMemberNickName(any()))
.thenThrow(new DuplicateMemberNicknameException());
아무 생각 없이 given - thenThrow로 했더니 "reason: no instance(s) of type variable(s) T exist so that void confirms to T"의 컴파일 에러가 발생합니다.
해결
Void methods can be used with Mockito’s doNothing(), doThrow(), and doAnswer() methods, making mocking and verifying intuitive:
저는 BDDMockito를 이용했으나, BDDMockito는 결국 Mockito를 감싸고 있는 형태입니다.
BDDMockito의 willDoNothing -> Mockito의 doNothing
BDDMockito의 willThrow -> Mockito의 doThrow
1. 만약 해당 메서드가 예외를 터트리지 않고 정상 진행되는 테스트를 작성하고 싶다면
willDoNothing - given
willDoNothing()
.given(memberService).validateMemberNickName(any());
2. 해당 메서드가 예외를 터트리는 테스트를 작성하고 싶다면
willThrow(해당 예외) - given
willThrow(new DuplicateMemberNicknameException())
.given(memberService).validateMemberNickName(any());
해당 메서드들을 이용해서 테스트 코드를 작성하면, void type의 메서드 역시 정상적으로 동작하는 것을 확인할 수 있었습니다.
출처
https://www.baeldung.com/mockito-void-methods
https://www.baeldung.com/mockito-exceptions
'개발 공부 > 스프링' 카테고리의 다른 글
[SpringBoot x JPA] Soft Delete #2 Insert (중복, Unique 제약조건, 인덱스의 관점에서) (0) | 2022.10.27 |
---|---|
[SpringBoot X AWS S3] AWS S3 PresigendURL 적용하고 업로드해보기 (1) | 2022.10.11 |
[Github Actions] CI 스크립트에 Redis 환경 추가하기 (0) | 2022.10.02 |
[SpringBoot x JPA] Soft Delete #1 Select (해당 Status 조건 추가 조회 방법) - @Where (1) | 2022.09.11 |
[SpringBoot x JPA] List 초기화에서 Builder 패턴 사용 시 NullPointerException (0) | 2022.08.08 |