[유닛 테스트] 3. Mockito

rin·2020년 4월 8일
1

유닛 테스트

목록 보기
3/4
post-thumbnail

Mockito

Mockito 공식 사이트를 번역해본다.
ref. https://site.mockito.org/

Intro

프로젝트 상태 : 릴리즈 노트
업테이트 정보 : 트위터, Google group의 메일 리스트를 통해 발표
Maven, Gradle 및 기타 빌드 시스템 설정에 대한 Mockito 다운로드 및 지침 : Central Repository, Bintray
모든 버전 문서 : javadoc.io (사이트는 최신 릴리즈 후 24시간 내에 업데이트)
Mockito 2 기능확인
Mockito 3.x는 Java 8+를 요구하지만 2.x에 비해 크게 달라진 점은 없다.

Why

  1. 깨끗하고 간단한 API로 테스트를 작성할 수 있다.
  2. 테스트 코드를 읽기 쉽고, 검증 오류가 발생하기 때문에 혼란스럽지 않다. [feature & motivations 알아보기]
  3. StackOverflow 커뮤니티는 Mockito를 Java용 최고의 Mocking 프레임 워크로 선정했다.
  4. BDD의 개발 창시자인 Dan North는 2008년에 다음과 같이 이야기 하였다. "우리는 JUnit4와 Mockito를 사용하자 결정했다. 왜냐면 JUnit4와 Mockito는 Java의 TDD와 Mocking의 미래라고 생각하기 때문이다."

How

선호하는 빌드 시스템을 사용해 'mockito-core' 라이브러리에 대한 dependency를 선언한다.

  1. Gradle을 사용할 시 다음처럼 수행할 수 있다.
repositories { jcenter() }
dependencies { testCompile "org.mockito:mockito-core:2.+" }
  1. Maven을 사용자 또한 mockito-core에 대한 종속성을 선언할 수 있다.
  2. Mockito는 자동으로 Bintray의 jcenter에 게시되고 Maven Central Repository에 동기화된다.
  3. 수동 종속성 관리(manual dependency management)를 수행하는 사용자는 Files tab 아래 Mockito의 Binary 저장소에서 직접 jar 를 다운로드 할 수 있다.

now you can verity interactions

import static org.mockito.Mockito.*;

// 인터페이스에 대한 목 객체 생성
List mockedList = mock(List.class);

// 목 객체 사용 - "unexpected interaction" exception이 발생하지 않는다.
mockedList.add("one");
mockedList.clear();

// 선택적이고, 명시적이며, 읽기 쉬운 verification을 수행한다.
verify(mockedList).add("one");
verify(mockedList).clear();

and stub method calss

// 인터페이스 뿐만 아니라 구체화된 클래스 또한 Mocking 할 수 있다.
LinkedList mockedList = mock(LinkedList.class);

//  get(0)을 수행할 시 return해 줄 값을 명시한다.
// 실제 실행 전에 Stubbing이 일어난다.
when(mockedList.get(0)).thenReturn("first");

// "first"
System.out.println(mockedList.get(0));

// index 999에 대해 stubbing하지 않았으므로 null이 출력된다.
System.out.println(mockedList.get(999));

More

주요 참조 문서 :
mock() / @Mock : Mock 만들기

  • Answer/MockSettings를 통해 동작하는 방법을 지정할 수 있다.
  • when() / given()는 mock이 어떻게 행동할지 지정한다.
  • 만약 제공된 Answers가 필요로 하는 것과 들어맞지 않는 경우 Answer interface를 확장하여 직접 작성할 수 있다.

spy() / @Spy : partial mocking, 실제 메소드가 호출되지만 verify와 stub가 가능하다.

  • @InjectMocks : @Spy 또는 @Mock Annotation이 달린 spies/mocks fields를 자동으로 주입한다.
  • verify() : 주어진 argument로 메소드가 호출되었는지 확인한다. 가변적인 argument에 대한 매칭을 사용할 수 있다. (e.g. any()를 통한 모든 표현)
  • 혹은 @Captor를 사용해 호출된 argument를 캡처할 수 있다.

BDDMockito를 이용해 행동 주도 개발(Behavior-Driven development) 구문을 작성할 수 있다.
dexmaker 덕분에 Android에서도 Mockito를 사용할 수 있게 되었다.

주요 개념 파악하기

ref. Mocks Aren't Stubs - 번역본
ref. https://eminentstar.github.io/2017/07/24/about-mock-test.html

Test Double

🤔 Test Double 이란?

테스팅에서 실제 객체를 대신하여 사용되는 모든 방법을 일컫는다.
실제 객체를 사용하지 어렵고 모호할 때, 이를 대신해줄 객체를 만들어 테스트 수행을 돕는다.
(이 이름은 영화의 Stunt Double-스턴트맨-이라는 개념에서 나왔다.)
🔎Dummy
Dummy 객체는 전달되기만하고 실제 사용되지는 않는다.
단지 인스턴스화된 객체가 필요한 경우(해당 객체의 기능이 필요하지 않을 때)
🔎Fake
Fake 객체는 실제로 동작하는 구현체가 있으나, 운영시에는 사용할 수 없는 간단한 형태이다.
여러개의 인스턴스를 대표할 수 있는 경우.
보통 List나 Map을 이용해서 DB같은 외부 의존 환경을 대체한다. 즉, 복잡한 로직or외부 서비스의 동작을 비교적 단순화하여 구현한 객체.
(e.g. in memory database)
🔎Stub
Dummy 객체가 마치 실제로 동작하는 것처럼보이게 만들어놓은 객체
Stub는 테스트 시 호출되면 미리 준비된 Answers로 응답하므로 테스트에 사용하기 위해 미리 프로그램된 것 이외의 것에 대해서는 응답하지 않는다.
🔎Spy
Stub의 역할을 가지면서 호출된 내용에 대해 테스트에서 확인하기 위한 약간의 정보를 기록하는 객체이다.
호출 여부를 감시해서 기록했다가 나중에 요청이 들어오면 해당 기록 정보를 전달해준다.
기록 정보 : 특정 객체가 사용됐는가? 그 객체의 예상된 메소드가 정상적으로 호출됐는가?
🔎Mock
Mock 객체는 호출에 대한 기대를 명세하고, 해당 내용에 따라 동작하도록 프로그래밍 된 객체이다.

Mock

상태 기반 테스트 vs. 행위 기반 테스트

  • 상태 기반 테스트
    • 특정한 메소드를 거친 후 객체의 상태에 대해 예상값과 비교
    • e.g. setId(1) === getId() ?
  • 행위 기반 테스트
    • 특정한 동작이 수행됐는가 여부를 확인
    • 메소드 리턴값이 없거나, 리턴값의 확인으로 예상대로 동작함을 보장할 수 없는 경우 사용
    • e.g 특정 argument에 대해 A인 경우에는 methodA가 호출-methodB는 호출X, B인 경우에는 methodA는 호출X-methodB만 호출 되는 상황이 있다고 가정하자. 이 때는 각각의 argument A, B에 대해 어떤 메소드가 호출되고/호출되지 않았는지 판단해야한다.
      즉, 행위를 점검하는 것으로 테스트 케이스를 만든다.
    • 예상하는 행위들을 미리 시나리오로 만들어 놓고 해당 시나리오대로 동작이 발생했는지 여부를 확인하는 것이 핵심이 된다.

Mock Object

일반적인 테스트 더블은 상태를 기반으로 테스트 케이스를 작성
Mock 객체는 행위를 기반으로 테스트 케이스를 작성한다.

📌Mockito와 행위 기반 테스트

Mockito는 Stub 작성과 verify가 중심을 이룬다.

  1. CreateMock : 인터페이스에 해당하는 Mock 객체 생성
  2. Stub : 필요한 경우 테스트에 필요한 Mock 객체의 동작 지정
  3. Exercise : 테스트 메소드 내에서 Mock 객체를 사용
  4. Verify : 메소드가 예상대로 호출됐는지 검증

Spy

실제 객체를 Mock으로 만들어 사용할 수 있다.
(너무 강력해서 문제 될 수도 있다..🤔)
부분 Mocking이라는 방법으로 서드파티 제품, 고칠 수 없는 라이브러리만 남아있는 코드 등에 대해서만 한정적으로 사용하는 걸 권장.
💁Mockito의 저자는 spy 기능을 쓰게 되면 그건 이미 뭔가 잘못된 코드를 건드리고 있는 증거라고 이야기했음

InjectMocks

ref. https://javadoc.io/doc/org.mockito/mockito-core/latest/org/mockito/InjectMocks.html
mock/spy 객체를 주입한다.
어노테이션으로 명시하여 반복적인 mock/spy 주입을 최소화한다.
Mockito는 생성자 주입, setter 주입, property 주입에 의해 순차적으로 mock 주입을 시도한다.
주입 시도 단계 중 하나라도 실패하면 전체가 실패하지만, mockito는 이를 보고하지 않는다. 즉, 의존성을 직접 주입해야한다.

Verify

ref. API document
ref. verify 사용 예시(요약)
특정 메소드의 호출에 대해 확인한다.
e.g. 단 1회 호출 됐는가?, 전혀 호출되지 않았는가?

Stubbing

테스트 작성자가 정해둔 행위로 실제 행위가 대치되는 것을 뜻한다.

profile
🌱 😈💻 🌱

2개의 댓글

comment-user-thumbnail
2022년 9월 20일

This is such a great resource that you are providing and you give it away for free. I love seeing blog that understand the value of providing a quality resource for free run 3

답글 달기
comment-user-thumbnail
2023년 1월 5일

I just started using Mockito for my unit tests and it has made my life so much easier! The syntax is easy to understand and the documentation is great. I highly recommend giving it a try if you're not already using it. Sartenes eléctricas con dobles caras

답글 달기