일반적인 테스트 | 모의 객체 테스트 | |
---|---|---|
테스트 대상 객체 | 실제 객체 | 모의 객체 |
검증 방식 | 상태(state) 검증 | 행위(behavior) 검증 |
셋업 방식 | 협력 객체 생성 및 최초 데이터 적재 | 모의 객체 생성 및 모의 객체에 기대되는 행위 선언 |
관심사 | 행위의 결과 | 객체간 메시지 통신 |
테스트 대역은 테스팅을 목적으로 진짜 객체대신 사용되는 모든 종류의 위장 객체를 지칭한다.
BDD Style Specification
- Title
- Narrative
- As a ~
- I want ~
- So that ~
- Acceptance Criteria
- Given
- When
- Then
출처: 모의객체는 스텁이 아니다
Mockito는 모의 객체 생성, 검증, 스텁을 지원하는 프레임워크이다.
깨끗하고 간단한 API로 테스트를 작성할 수 있다.
테스트 코드의 가독성이 높다.
명확한 검증 오류가 발생한다.
plugins {
id 'java'
}
repositories {
mavenCentral()
}
dependencies {
// JUnit 5
testImplementation(platform('org.junit:junit-bom:5.7.2'))
testImplementation('org.junit.jupiter:junit-jupiter')
// Mockito
testImplementation('org.mockito:mockito-core:3.11.2')
testImplementation('org.mockito:mockito-junit-jupiter:3.11.2')
}
test {
useJUnitPlatform()
}
mock()
/@Mock
: 모의 객체를 생성한다.Answer
/MockSettings
를 통해 동작 방식을 명시할 수 있다.when()
/given()
을 이용해 모의 객체가 어떻게 동작해야 하는지 명시할 수 있다.Answer
를 확장할 수도 있다.spy()
/@Spy
: 스파이를 생성한다.@InjectMocks
: @Spy
또는 @Mock
으로 선언된 필드에 객체를 자동으로 주입한다.verify()
: 메서드가 주어진 인자로 호출되었는지 검증한다.any()
)@Captor
를 이용하여 어떤 인자로 호출되었는지 확인할 수 있다.BDDMockito
는 행위 주도 개발(BDD) 구문을 지원한다.Mockito.when
↔ BDDMockito.given
Mockito.verify
↔ BDDMockito.then
// mock creation
List mockedList = mock(List.class);
// using mock object - it does not throw any "unexpected interaction" exception
mockedList.add("one");
mockedList.clear();
// selective, explicit, highly readable verification
verify(mockedList).add("one");
verify(mockedList).clear();
// you can mock concrete classes, not only interfaces
LinkedList mockedList = mock(LinkedList.class);
// stubbing appears before the actual execution
when(mockedList.get(0)).thenReturn("first");
// the following prints "first"
System.out.println(mockedList.get(0));
// the following prints "null" because get(999) was not stubbed
System.out.println(mockedList.get(999));
Keep the testing code compact and readable.
테스트 코드를 간결하고 가독성 좋게 유지하라.
Do not mock types you don’t own.
당신이 소유하지 않은 타입은 모의 객체로 만들지 말라.
Don’t mock value objects.
값 객체는 모의 객체로 만들지 말라.
Don’t mock everything.
모든 것을 모의 객체로 만들지 말라.
mock()
또는 spy()
를 이용해서 생성한다.@ExtendWith(MockitoExtension.class)
로 익스텐션을 등록하고 @Mock
또는 @Spy
애노테이션으로 모의 객체를 생성할 대상을 지정한다.Mockito.when(mock.action())
.thenReturn(true)
.thenThrow(Exception.class)
BDDMockito.given(mock.action())
.willReturn(true)
.willThrow(Exception.class)
null
모의 객체의 역할 중 하나는 실제로 모의 객체가 불렸는지 검증하는 것이다.
예시) 모의 객체 mock
에 action()
메서드가 호출되었음을 검증하기
Mockito.verify(mock).action()
BDDMockito.then(mock).should().action()
should()
로 모의 객체의 메서드가 불려야 한다고 설정하고, should()
메서드 다음에 실제로 불려야 할 메서드를 지정한다.정확한 값이 아니라 메서드가 불렸는지 여부가 중요하다면 인자 매칭을 이용한다.
메서드 호출 횟수를 검증하려면 should()
메서드에 다음과 같은 메서드를 인자로 전달한다.
only()
: 한 번만 호출
times(int)
: 지정한 횟수만큼 호출
never()
: 호출하지 않음
atLeast(int)
: 적어도 지정한 횟수만큼 호출
atLeastOnce()
: atLeast(1)
과 동일
atMost(int)
: 최대 지정한 횟수만큼 호출
ArgumentMatchers
클래스를 이용하면 정확하게 일치하는 값 대신 임의의 값에 일치하도록 설정할 수 있다.any*()
: 해당 타입에 대한 임의 값 일치any()
, anyInt()
, anyShort()
, anyLong()
, anyByte()
, anyChar()
, anyDouble()
, anyFloat()
, anyBoolean()
, anyString()
, anyList()
, anySet()
, anyMap()
, anyCollection()
matches(String|Pattern)
: 정규 표현식을 이용한 String
값 일치 여부eq()
: 특정 값에 대한 일치ArgumentCaptor
를 사용하면 메서드 호출 여부를 검증하는 과정에서 실제 호출할 때 전달한 인자를 보관할 수 있다.capture()
메서드를 전달하고getValue()
메서드로 실제 인자 값을 가져와서 검증에 사용한다.