Mock
- 실제 모듈과 비슷하게 만들어놓은 object- 테스트를 위한 환경 구축에 대한 시간이 오래 걸릴 경우, 아직 필요한 모듈이 개발이 안된 경우 등에 mock 생성
- 테스트가 특정 경우나 순간적인 의존성을 필요로 할 때
- 테스트 시간이 특히 오래 걸릴 때
Test double
- original object를 사용해서 테스트가 힘든 경우 대신해서 테스트를 진행할 수 있게 만든 object(Mock과 의미가 같다고 봄)Dummy object
- 인스턴스화 될 수준으로만 구현한 object- object의 메소드 사용까지는 보장하지 않는 수준.
Test stub
- Dummy object가 실제로 동작하는 것처럼 보이게 만든 object.- Dummy와 다른 점은 특정 메소드에서 특정 값을 리턴해주는 작업등을 한다는 점.
- Dummy보다 발전된 형태라고 보면 됨
Fake Object
- Test stub 을 넘어선 수준의 실제처럼 보이는 object- Test를 위해 다른 객체들과의 의존성을 제거하기 위해 사용되는 object
Test Spy
- object의 호출 여부를 확인하고 기록을 전달해주는 테스트 더블이 테스트 스파이.- 특정 메소드의 정상호출 여부 확인을 목적으로 함.
- 특수한 경우를 제외하고는 잘 쓰이지 않음.
Mock Framework
Mock object란 해위를 검증하기 위해 사용되는 objectEasyMock, jMock 은 따로 시간날때 보도록 하고 mockito로 넘어간다.
Mockito
- 전통적인 상태 기반 테스트를 지향한다.- Mock framework는 어때야 하는가?
- 단순해야한다.
- Mock framework 를 DSL로 만들지 말자. 복잡해진다.
- 문자열을 메소드 대신에 사용하지 말자.
- 읽기 어려운 anonymous inner class를 사용하지 말자
- 리팩토링이 어려우면 안된다.
- 그렇다면 Mockito의 차별점은??
- 테스트 그 자체에 집중한다.
- 테스트 스텁을 만드는 것과 검증을 분리시켰다.
- Mock 만드는 방법을 단일화
- 테스트 스텁을 만들기 쉽다.
- API가 간단하다.
- Framework가 지원해주지 않으면 안되는 코드를 최대한 배제했다.
- 실패 시에 발생하는 stack trace이 깔끔하다.
mockito는 stub, verify가 중심을 이루고 다음과 같은 순서로 진행된다.
CreateMock - 인터페이스에 해당하는 Mock object를 만든다.
Stub - 테스트에 필요한 mock object의 동작을 지정한다.(필요시에만)
Exercise - 테스트 메소드 내에서 mock object를 사용한다.
Verify - 메소드가 예상되로 호출되었는지 검증한다.
1. Mock 객체 만들기
Mockito.mock(타겟 인터페이스) 로 생성한다.Mockito class의 mock 메소드를 이용해서 인터페이스나 클래스를 지정한다. static import로 mockito class를 처리.
2. 예상값 지정
mockito는 예상값을 지정하는 것이 아니라 필요 시에 테스트 스텁만 만들고 나중에 호출 여부를 확인한다.스텁 -> 수행 -> 검증 의 단계로 이루어지기 때문에 예상값 지정 부분이 없다.
3. 테스트에 사용할 스텁 만들기
when(mock 객체의 메소드).thenReturn(리턴 값);when(mock 객체의 메소드).thenThrow(예외);
스텁은 필요할 때만 만드는 것이 원칙이다. 호출 여부만 검증할 때에는 사용하지 않는다.
4. 검증
verify(mock 객체).Mock객체의 메소드;verify(mock 객체, 호출횟수지정 메소드).Mock객체의 메소드;
호출횟수 지정해서(optional) 특정 메소드가 호출됐는지 확인한다.
호출횟수 지정 메소드 종류
times(n) - n번 호출돼는지 확인. n=0은 times를 지정하지 않았을 때와 동일
never - 호출되지 않았어야 함
atLeastOnce - 최소 한번 호출되었어야 함
atLeast(n) - 적어도 n번 호출되었어야 함
atMost(n) - 최대 n번 이상 호출되면 안 됨
4-a. Argument Matcher
verify(mockedList, times(5)).add(any());
위에서와 같이 add의 parameter로 어떤것이 오더라도 상관없이 5번 호출에 대해서 검증할 때 사용할 수 있는것이 argument matcher.
4-b. InOrder
List firstMock = mock(List.class);
List secondMock = mock(List.class);
firstMock.add("item1");
secondMock.add("item2");
InOrder inOrder = inOrder(firstMock, secondMock);
inOrder.verify(secondMock).add("item2");
inOrder.verify(firstMock).add("item1");
위와 같이 순서대로 호출이 되는가 검증할 수 있음.
Mock 사용시 유의사항
Mock framework가 정말 필요한지 따져본다
- mock이 필요한 부분이 나오는게 아니라 mock을 사용하기 위한 현상으로 뭔가 뒤집힌 상태가 되면 안된다.mock을 사용한다는것은 fragile testcase가 되기 쉬움. 따라서 mock 의존성이 적게 작성하는게 좋은 편이다.
투자 대비 수익(ROI)가 확실할 때만 사용한다.
- 장기적으로 프로젝트를 진행할수록 mock 객체도 늘어나고 할 텐데 어떤걸 mock으로 만들건지 생각해보고 작성하자.어떤 Mock framework를 사용하느냐가 핵심적인 문제가 아니다.
- 어떤 framework를 사용하더라도 익숙해지려면 비용이 들고, 사실 대부분 간단한 mock 객체만으로도 충분한 경우가 많다.Mock은 Mock일 뿐이다.
- Mock으로 테스트가 잘 작동하더라도 실제 객체를 집어넣었을 때 잘 작동할리란 보장은 없다.초반부터 실제 객체를 사용할 수 있다면 그리고 그 비용이 크지 않다면 mock을 사용할 필요가 없다.