mockito는 단위 테스트를 위한 java mocking framework이다.
어디다 쓰냐면... 단위 테스트를 해야 하는데 데이터베이스에서 데이터를 가져와야 할 경우 테스트 환경에 따라 각각 다른 데이터가 조회될 수 있는데 예를 들어 A라는 고객이 1번_테스트_데이터베이스에서는 ID가 1인데 2번_테스트_데이터베이스에서는 5번일 수 있다. 이럴 경우 상황에 따라선 테스트의 단위가 깨지게 되는데, mockito를 이용하면 입력한 값을 리턴하게 되므로 동일한 환경으로 테스트할 수 있게 된다.
mockito를 사용하려면 dependencies를 추가해야 하지만 SpringBoot를 사용하면 gradle에 spring-boot-starter-test를 추가하게 되는데, 그 안에 mockito-junit-jupiter 패키지가 포함되어 있다.
만약 springboot를 사용하지 않는다면 다음 패키지를 추가하면 된다
dependencies {
....
testCompile group: 'org.mockito', name: 'mockito-junit-jupiter', version: '3.1.0'
....
}
mock에는 2개의 어노테이션이 있는데 @Mock 과 @InjectMocks 가 있다. 둘의 차이는 다음과 같다
@Mock: mock 객체를 만들어 반환
@InjectMocks: @Mock이나 @Spy 객체를 자신의 멤버 클래스와 일치하면 주입
@InjectMocks를 사용하는 경우를 예를 들면 MemberService 안에 MemberRepository 가 있을 때
MemberRepository가 주입받고 난 후에 MemberService 가 생성되어야 하는데 이때 사용에 용이하다.
간단하게 코드로 보면 다음과 같다
# MemberService 클래스
@Service
@RequiredArgsConstructor
public class MemberService {
private final MemberRepository memberRepository;
public List<Member> findMemberAll() {
return memberRepository.findAll();
}
}
# MemberRepository 클래스
@Repository
public class MemberRepository {
@PersistenceContext
private EntityManager em;
public void persist(Member member) {
em.persist(member);
}
public List<Member> findAll() {
return em.createQuery("select m from Member m", Member.class)
.getResultList();
}
}
MemberService의 findMemberAll() 메서드를 테스트할 경우
@ExtendWith(MockitoExtension.class)
public class MemberServiceMockTest {
@Mock
private MemberRepository memberRepository;
@InjectMocks
private MemberService memberService;
...
}
그럼 이제 사용법에 대해 알아보자.
우선 Repository만 테스트하는 것을 만들어본다
given 을 이용해 memberRepository.findAll() 을 조회할 경우 리턴할 값을 지정해둔다.
이후 기능을 수행하도록 코드를 짠 뒤에 리턴된 결괏값을 비교하여 테스트가 성공하는지 확인한다.
...
@Mock
private MemberRepository memberRepository;
@Test
@DisplayName("mockito 레파지토리 테스트")
void mockMemberRepositoryTest() {
//given
Member member1 = Member.builder()
.name("mock유저1")
.age(20)
.build();
List<Member> members = new ArrayList<>();
members.add(member1);
given(memberRepository.findAll()).willReturn(members);
//when
List<Member> findMembers = memberRepository.findAll();
System.out.println("findMembers = " + findMembers);
//then
Assertions.assertEquals(1, findMembers.size());
Assertions.assertEquals(member1.getName(), findMembers.get(0).getName());
}
...
수행하면 테스트는 성공하고 다음의 내용을 콘솔에 보여준다
findMembers = [Member(id=null, name=mock유저1, age=20)]
그럼 이번에 Service를 테스트해보자
...
@Mock
private MemberRepository memberRepository;
@InjectMocks
private MemberService memberService;
@Test
@DisplayName("mockito 서비스 테스트")
void mockMemberServiceTest() {
//given
Member member1 = Member.builder()
.name("mock유저1")
.age(20)
.build();
List<Member> members = new ArrayList<>();
members.add(member1);
given(memberRepository.findAll()).willReturn(members);
//when
List<Member> findMembers = memberService.findMemberAll();
System.out.println("memberAll = " + findMembers);
//then
Assertions.assertEquals(member1.getName(), findMembers.get(0).getName());
}
...
테스트는 성공으로 되고 다음의 내용을 콘솔로 보여준다
memberAll = [Member(id=null, name=mock유저1, age=20)]
기타)
@InjectMocks를 일반 @Mock으로 변경해 테스트하면 어떻게 될지 확인해봤다.
다음과 같은 에러가 발생한다
java.lang.IndexOutOfBoundsException: Index: 0, Size: 0
at java.base/java.util.LinkedList.checkElementIndex(LinkedList.java:559)
at java.base/java.util.LinkedList.get(LinkedList.java:480)
at kr.sample.demo.service.MemberServiceMockTest.mockMemberServiceTest(MemberServiceMockTest.java:72)
...
Suppressed: org.mockito.exceptions.misusing.UnnecessaryStubbingException:
Unnecessary stubbings detected.
Clean & maintainable test code requires zero unnecessary code.
Following stubbings are unnecessary (click to navigate to relevant line of code):
1. -> at kr.sample.demo.service.MemberServiceMockTest.mockMemberServiceTest(MemberServiceMockTest.java:63)
Please remove unnecessary stubbings or use 'lenient' strictness. More info: javadoc for UnnecessaryStubbingException class.
Unnecessary stubbings detected.를 표기하며 에러가 발생한다.
해당 에러가 발생한 곳은 given() 구절로, 불필요한 stubbings 을 한 것이 원인이라 표시되어 있다.
현 테스트에 불필요한 코드가 들어있다는 의민데, 해당 옵션을 끄려면 다음 설정을 하면 된다.
@MockitoSettings(strictness = Strictness.LENIENT)
class ...
그런데 왠만하면 끄지말고 진행하는 것을 추천한다.
끝.
샘플은 아래 github에서 확인할 수 있다.
https://github.com/lemontia/mockitoTest
'공부 > 프로그래밍' 카테고리의 다른 글
[java] NoSuchMethodError MockitoLogger 에러가 날 때 (0) | 2020.02.18 |
---|---|
[intellij] junit 으로 작성한 테스트가 gradle 로 실행될때 (3) | 2020.02.13 |
[spring security oauth] 403이 아닌 406 에러가 나는 경우 (Accept 설정에 따른 문제) (0) | 2020.01.30 |
[java] builder 패턴, 객체를 안전하게 생성하기 (0) | 2020.01.29 |
[react, springboot] react 와 spring boot 로 구성하기, 묶어 build 하기 (21) | 2020.01.19 |
댓글