작성
·
772
0
개인적으로 Mock은 상태를 (내가 작성한 값을) 반환하는 것, Stub은 상태가 (내가 작성한 구현대로) 반환되는 것이라고 정의를 내리고 있습니다. 이와 같은 표현으로 차이를 이해하고 있어도 괜찮을까요?
아니면 좀 더 명확한 표현이 있을까요?
Mock 객체에 우리가 원하는 행위를 정의(Stubbing)하면,
그 객체는 이제 Mock 객체라고 해야하나요 아니면 Stub 객체라고 해야하나요?
아래 코드를 보고 Mock 객체인 mailSendClient의 send 메서드에 대한 Stubbing이 이루어졌다. 라고 하면 맞는 표현인가요?
@ExtendWith(MockitoExtension.class)
class MailServiceTest {
@Mock
private MailSendClient mailSendClient;
@Mock
private MailSendHistoryRepository mailSendHistoryRepository;
@InjectMocks
private MailService mailService;
@DisplayName("메일 전송 테스트")
@Test
void sendMail() {
// given
when(mailSendClient.send(anyString(), anyString(), anyString(), anyString()))
.thenReturn(true);
// when
boolean result = mailService.sendMail("", "", "", "");
// then
assertThat(result).isTrue();
// verify(mailSendHistoryRepository, times(1))
// .save(any(MailSendHistory.class));
}
}
mailSendClient는 Mocking하더라도 상태검증을 할 수도 있고, Stubbing하더라도 행위검증을 할 수도 있지 않나요?
이 둘의 차이는 Mockist와 Classicist의 차이이지, Mock과 Stub의 차이라고 할 수 없지 않을까요?
답변 1
1
안녕하세요, 송유현 님!
하나씩 답변 드리겠습니다.
1. 개인적으로 Mock은 상태를 (내가 작성한 값을) 반환하는 것, Stub은 상태가 (내가 작성한 구현대로) 반환되는 것이라고 정의를 내리고 있습니다. 이와 같은 표현으로 차이를 이해하고 있어도 괜찮을까요?
아니면 좀 더 명확한 표현이 있을까요?
'값을 반환한다'와 '구현대로 반환된다'라고 하셨지만, Stub도 내가 구현한대로 값을 반환하는 것에 초점이 맞춰져 있습니다.
Stub은 테스트 검증을 위해 미리 지정해놓은 값을 반환합니다.
테스트할 기능 외에는 신경쓰지 않습니다.
반환된 값에 초점을 맞추어 검증합니다.
Mock은 특정 행동을 지정하고, 테스트 과정에서 그 행동이 의도대로 이루어졌는지를 검증합니다.
정리하면, Stub은 상태 검증
, Mock은 행위 검증
의 시각으로 보는 것이 좋습니다.
2. Mock 객체에 우리가 원하는 행위를 정의(Stubbing)하면,
그 객체는 이제 Mock 객체라고 해야하나요 아니면 Stub 객체라고 해야하나요?
Mock 객체에서 Stubbing을 정의할 수 있지만, 애초에 행위 검증을 위해 Mock 객체를 만들었다고 볼 수 있기 때문에 Mock 객체로 보는 것이 맞겠습니다.
사실 Mock 객체를 만들어서 어떻게 검증하는지까지 보아야 하는데, 만약 Mock 객체라고 만들었지만 검증 단계에서 값(상태)을 위주로 검증하고 있다면 사실 Stub 객체처럼 사용되었다라고 볼 수 있을 것이고, 객체의 행동에 대한 수행 과정을 검증하고 있다면 Mock 객체라고 이해하면 될 것 같아요.
3. 아래 코드를 보고 Mock 객체인 mailSendClient의 send 메서드에 대한 Stubbing이 이루어졌다. 라고 하면 맞는 표현인가요?
네, 맞습니다. when(mailSendClient.send(...)).thenReturn(true);
에서 특정 값을 반환하도록 설정(Stubbing)하였고, 검증 단계에서도 true 라는 값을 검증하고 있습니다.
추가적으로, 주석 처리되어 있는 verify(mailSendHistoryRepository, times(1)).save(any(MailSendHistory.class));
는 값이 아니라 save() 메서드의 행위를 중심으로 검증하고 있기 때문에 mocking이라고 볼 수 있겠습니다.
4. mailSendClient는 Mocking하더라도 상태검증을 할 수도 있고, Stubbing하더라도 행위검증을 할 수도 있지 않나요?
이 둘의 차이는 Mockist와 Classicist의 차이이지, Mock과 Stub의 차이라고 할 수 없지 않을까요?
Mocking/Stubbing과 Mockist/Classicist는 아예 다른 개념입니다.
Mock 객체를 Stubbing하여 값 검증에만 쓰면 사실 Stub 객체처럼 사용하여 상태 검증을 한 것이고, 행위 검증을 한다면 Mocking했다고 할 수 있습니다. (즉, 객체를 생성해서 어떻게 검증을 하고 있는지까지 확인해보아야 합니다.)
Classicist는 상태 검증인지 행위 검증인지, 어떻게 검증할 것인지에 대한 방법과 상관 없이, 정말 필요한 경우를 제외하고는 최대한 Mock 객체가 아닌 실제 객체를 사용하여 검증해야 한다는 주장입니다.
즉, Mocking과 Stubbing은 테스트 도구의 사용 방법에 대한 개념이며, Mockist와 Classicist는 Mock/Stub과 같은 테스트 더블 자체를 주요 테스트 전략으로 사용하는 것이 더 나은지 아닌지를 이야기하는 테스트 철학에 관한 개념이기 때문에 전혀 다른 레벨로 이해하셔야 합니다.
도움이 되셨기를 바랍니다.
감사합니다. 🙂
그렇다면 어떻게 검증했는지 (행위인지 상태인지) 보지 않으면 Mocking을 한건지 Stubbing을 한 건지 알 수 없다고 하는 게 맞을까요?