1. BDD..? 그게 무엇일까??
BDD, Behavior-Driven Development 로 행위 주도 개발을 뜻한다.
이는 기존의 TDD에서 확장된 개발론으로, "시나리오를 기반으로 한 행위"에 더 많은 관심을 기울인다.
이 시나리오는 다음과 같은 형식을 따르며, 이를 통해 실제 테스트가 이루어진다.
Given(초기 상황) → When(행위, 이벤트) → Then(기대 결과)
2. 그럼 BDDMockito와 기존의 Mockito를 사용했을 때 차이점을 살펴보자.
BDD 방식과 Mockito의 기능성을 결합하여,
BDD의 "Given-When-Then" 구조 스타일의 테스트 작성을 지원하도록 만든 것이 바로 `BDDMockito`이라고 정의를 했다.
그럼 기존의 Mockito와 BDDMockito의 코드 차이점을 살펴보자.
(https://jaehoney.tistory.com/220 에 좋은 예제 코드가 있어 가지고 와 소개하였다.)
(해당 코드는 StudyService 클래스의 createNewStudy 메소드를 테스트하는 예제이다.)
[Mockito 코드]
@ExtendWith(MockitoExtension.class)
public class StudyServiceTest {
@Mock
MemberService memberService;
@Mock
StudyRepository studyRepository;
@Test
void createStudyService() {
// Given
StudyService studyService = new StudyService(memberService, studyRepository);
Member member = new Member();
member.setId(1L);
member.setEmail("keesun@gmail.com");
Study study = new Study(10, "테스트");
when(memberService.findById(1L)).thenReturn(Optional.of(member));
when(studyRepository.save(study)).thenReturn(study);
// When
studyService.createNewStudy(1L, study);
// Then
assertEquals(member, study.getOwner());
verify(memberService, times(1)).notify(study);
verifyNoMoreInteractions(memberService);
}
}
코드를 차근차근 살펴보자.
Given
- StudyService 객체를 생성하는데, 필요한 인자로 모의 memberService, studyRepository를 전달한다.
- 테스트에 사용할 Member, Study 객체를 생성하고 초기화한다.
- memberService의 findById 메소드가 호출될 때 Optional.of(member)를 반환하도록 모의한다.
- StudyRepository의 save 메소드가 호출될 때 study 객체를 반환하도록 모의한다.
*이 때, 문제가 발생한다.
초기 상황을 정의하는 given 에서 when().thenReturn()이 사용이 되고 있다.
하지만 코드를 작성하고 읽는 측면에서 "given에서 when()을 사용하는 게 맞나?" 모순이 발생한 것이다.
이러한 문제를 해결하기 위해 BDDMockito를 사용한다.
When
- MemberId와 study 객체를 인자로, createNewStudy 메소드를 호출한다.
Then
- member 객체와 study 객체의 소유자가 같은지 확인한다.
- memberService의 notify 메소드가 한 번 호출되었는지 확인한다.
- memberService에서 notify 메소드 호출 외에, 다른 호출이 전혀 없었는지 확인한다.
[BDDMockito 코드]
@Test
void createNewStudy_shouldSameStudy() {
// Given
StudyService studyService = new StudyService(memberService, studyRepository);
Member member = new Member();
member.setId(1L);
member.setEmail("keesun@gmail.com");
Study study = new Study(10, "테스트");
given(memberService.findById(1L)).willReturn(Optional.of(member));
given(studyRepository.save(study)).willReturn(study);
// When
studyService.createNewStudy(1L, study);
// Then
assertEquals(member, study.getOwner());
then(memberService).should(times(1)).notify(study);
then(memberService).shouldHaveNoMoreInteractions();
}
차이점을 위주로 코드를 살펴보자.
Given
// Mockito
when(memberService.findById(1L)).thenReturn(Optional.of(member));
when(studyRepository.save(study)).thenReturn(study);
// BDDMockito
given(memberService.findById(1L)).willReturn(Optional.of(member));
given(studyRepository.save(study)).willReturn(study);
Then
// Mockito
verify(memberService, times(1)).notify(study);
verifyNoMoreInteractions(memberService);
// BDDMockito
then(memberService).should(times(1)).notify(study);
then(memberService).shouldHaveNoMoreInteractions();
기능적으로는 앞의 Mockito 코드와 동일하다.
하지만 Mockito 스타일에서 BDDMockito 스타일로 변경하면서
테스트 코드가 더 자연스러워져 읽기 쉽고, 테스트의 의도를 더 명확히 드러내는 코드가 되었다.
이로써 테스트의 가독성을 높이고, 이해하기 쉬운 테스트 코드 작성이 가능해졌다.
'Test' 카테고리의 다른 글
서비스 로직 Test 작성하기 (feat. Mockito) (1) | 2024.06.10 |
---|